diff --git a/data/skins/default/sheet.png b/data/skins/default/sheet.png index 8863dce75..ce6fc2038 100644 Binary files a/data/skins/default/sheet.png and b/data/skins/default/sheet.png differ diff --git a/data/skins/default/skin.xml b/data/skins/default/skin.xml index 3c9c5cb40..55aea81f8 100644 --- a/data/skins/default/skin.xml +++ b/data/skins/default/skin.xml @@ -6,6 +6,9 @@ + + + @@ -224,8 +227,14 @@ - - + + + + + + + + @@ -617,16 +626,59 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/ui/document_view.cpp b/src/app/ui/document_view.cpp index c47d55cfb..0f5a37ba9 100644 --- a/src/app/ui/document_view.cpp +++ b/src/app/ui/document_view.cpp @@ -191,13 +191,7 @@ void DocumentView::getDocumentLocation(DocumentLocation* location) const std::string DocumentView::getTabText() { - std::string str = m_document->name(); - - // Add an asterisk if the document is modified. - if (m_document->isModified()) - str += "*"; - - return str; + return m_document->name(); } WorkspaceView* DocumentView::cloneWorkspaceView() diff --git a/src/app/ui/main_window.cpp b/src/app/ui/main_window.cpp index 5ccc20671..322b368a5 100644 --- a/src/app/ui/main_window.cpp +++ b/src/app/ui/main_window.cpp @@ -294,6 +294,22 @@ void MainWindow::clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons } } +void MainWindow::clickClose(Tabs* tabs, TabView* tabView) +{ + DocumentView* docView = dynamic_cast(tabView); + if (!docView) + return; + + UIContext* context = UIContext::instance(); + context->setActiveView(docView); + context->updateFlags(); + + Command* close_file_cmd = + CommandsModule::instance()->getCommandByName(CommandId::CloseFile); + + context->executeCommand(close_file_cmd, NULL); +} + void MainWindow::mouseOverTab(Tabs* tabs, TabView* tabView) { // Note: tabView can be NULL @@ -307,4 +323,15 @@ void MainWindow::mouseOverTab(Tabs* tabs, TabView* tabView) } } +bool MainWindow::isModified(Tabs* tabs, TabView* tabView) +{ + if (DocumentView* docView = dynamic_cast(tabView)) { + Document* document = docView->getDocument(); + return document->isModified(); + } + else { + return false; + } +} + } // namespace app diff --git a/src/app/ui/main_window.h b/src/app/ui/main_window.h index 2f1c706c6..8ace90c74 100644 --- a/src/app/ui/main_window.h +++ b/src/app/ui/main_window.h @@ -61,8 +61,10 @@ namespace app { void popTimeline(); // TabsDelegate implementation. - void clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons); - void mouseOverTab(Tabs* tabs, TabView* tabView); + void clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons) override; + void clickClose(Tabs* tabs, TabView* tabView) override; + void mouseOverTab(Tabs* tabs, TabView* tabView) override; + bool isModified(Tabs* tabs, TabView* tabView) override; protected: bool onProcessMessage(ui::Message* msg) override; diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp index b0861b60f..f05faebf0 100644 --- a/src/app/ui/skin/skin_theme.cpp +++ b/src/app/ui/skin/skin_theme.cpp @@ -583,20 +583,26 @@ void SkinTheme::onRegenerate() else if (ruleName == "icon") { if (align) (*style)[StyleSheet::iconAlignRule()] = css::Value(align); if (part_id) (*style)[StyleSheet::iconPartRule()] = css::Value(part_id); + + const char* x = xmlRule->Attribute("x"); + const char* y = xmlRule->Attribute("y"); + + if (x) (*style)[StyleSheet::iconXRule()] = css::Value(strtol(x, NULL, 10)); + if (y) (*style)[StyleSheet::iconYRule()] = css::Value(strtol(y, NULL, 10)); } else if (ruleName == "text") { if (color_id) (*style)[StyleSheet::textColorRule()] = css::Value(color_id); if (align) (*style)[StyleSheet::textAlignRule()] = css::Value(align); - const char* padding_left = xmlRule->Attribute("padding-left"); - const char* padding_top = xmlRule->Attribute("padding-top"); - const char* padding_right = xmlRule->Attribute("padding-right"); - const char* padding_bottom = xmlRule->Attribute("padding-bottom"); + const char* l = xmlRule->Attribute("padding-left"); + const char* t = xmlRule->Attribute("padding-top"); + const char* r = xmlRule->Attribute("padding-right"); + const char* b = xmlRule->Attribute("padding-bottom"); - if (padding_left) (*style)[StyleSheet::paddingLeftRule()] = css::Value(strtol(padding_left, NULL, 10)); - if (padding_top) (*style)[StyleSheet::paddingTopRule()] = css::Value(strtol(padding_top, NULL, 10)); - if (padding_right) (*style)[StyleSheet::paddingRightRule()] = css::Value(strtol(padding_right, NULL, 10)); - if (padding_bottom) (*style)[StyleSheet::paddingBottomRule()] = css::Value(strtol(padding_bottom, NULL, 10)); + if (l) (*style)[StyleSheet::paddingLeftRule()] = css::Value(strtol(l, NULL, 10)); + if (t) (*style)[StyleSheet::paddingTopRule()] = css::Value(strtol(t, NULL, 10)); + if (r) (*style)[StyleSheet::paddingRightRule()] = css::Value(strtol(r, NULL, 10)); + if (b) (*style)[StyleSheet::paddingBottomRule()] = css::Value(strtol(b, NULL, 10)); } xmlRule = xmlRule->NextSiblingElement(); diff --git a/src/app/ui/skin/style.cpp b/src/app/ui/skin/style.cpp index 10e734322..aa6b904c3 100644 --- a/src/app/ui/skin/style.cpp +++ b/src/app/ui/skin/style.cpp @@ -107,6 +107,9 @@ void IconRule::onPaint(ui::Graphics* g, const gfx::Rect& bounds, const char* tex else y = bounds.y; + x += m_x; + y += m_y; + g->drawRgbaSurface(bmp, x, y); } @@ -120,6 +123,8 @@ Rules::Rules(const css::Query& query) : css::Value backgroundRepeat = query[StyleSheet::backgroundRepeatRule()]; css::Value iconAlign = query[StyleSheet::iconAlignRule()]; css::Value iconPart = query[StyleSheet::iconPartRule()]; + css::Value iconX = query[StyleSheet::iconXRule()]; + css::Value iconY = query[StyleSheet::iconYRule()]; css::Value textAlign = query[StyleSheet::textAlignRule()]; css::Value textColor = query[StyleSheet::textColorRule()]; css::Value paddingLeft = query[StyleSheet::paddingLeftRule()]; @@ -138,10 +143,14 @@ Rules::Rules(const css::Query& query) : } if (iconAlign != none - || iconPart != none) { + || iconPart != none + || iconX != none + || iconY != none) { m_icon = new IconRule(); m_icon->setAlign((int)iconAlign.number()); m_icon->setPart(StyleSheet::convertPart(iconPart)); + m_icon->setX((int)iconX.number()*ui::guiscale()); + m_icon->setY((int)iconY.number()*ui::guiscale()); } if (textAlign != none diff --git a/src/app/ui/skin/style.h b/src/app/ui/skin/style.h index 8c8ebccfb..6fcb28a82 100644 --- a/src/app/ui/skin/style.h +++ b/src/app/ui/skin/style.h @@ -85,6 +85,8 @@ namespace app { void setAlign(int align) { m_align = align; } void setPart(const SkinPartPtr& part) { m_part = part; } + void setX(int x) { m_x = x; } + void setY(int y) { m_y = y; } SkinPartPtr getPart() { return m_part; } @@ -94,6 +96,7 @@ namespace app { private: int m_align; SkinPartPtr m_part; + int m_x, m_y; }; class Rules { diff --git a/src/app/ui/skin/style_sheet.cpp b/src/app/ui/skin/style_sheet.cpp index 7c209eb23..573c6a712 100644 --- a/src/app/ui/skin/style_sheet.cpp +++ b/src/app/ui/skin/style_sheet.cpp @@ -26,6 +26,8 @@ css::Rule StyleSheet::m_backgroundPartRule("background-part"); css::Rule StyleSheet::m_backgroundRepeatRule("background-repeat"); css::Rule StyleSheet::m_iconAlignRule("icon-align"); css::Rule StyleSheet::m_iconPartRule("icon-part"); +css::Rule StyleSheet::m_iconXRule("icon-x"); +css::Rule StyleSheet::m_iconYRule("icon-y"); css::Rule StyleSheet::m_textAlignRule("text-align"); css::Rule StyleSheet::m_textColorRule("text-color"); css::Rule StyleSheet::m_paddingLeftRule("padding-left"); @@ -41,6 +43,8 @@ StyleSheet::StyleSheet() m_sheet->addRule(&m_backgroundRepeatRule); m_sheet->addRule(&m_iconAlignRule); m_sheet->addRule(&m_iconPartRule); + m_sheet->addRule(&m_iconXRule); + m_sheet->addRule(&m_iconYRule); m_sheet->addRule(&m_textAlignRule); m_sheet->addRule(&m_textColorRule); m_sheet->addRule(&m_paddingLeftRule); diff --git a/src/app/ui/skin/style_sheet.h b/src/app/ui/skin/style_sheet.h index 3e011251f..01bfc6926 100644 --- a/src/app/ui/skin/style_sheet.h +++ b/src/app/ui/skin/style_sheet.h @@ -41,6 +41,8 @@ namespace app { static css::Rule& backgroundRepeatRule() { return m_backgroundRepeatRule; } static css::Rule& iconAlignRule() { return m_iconAlignRule; } static css::Rule& iconPartRule() { return m_iconPartRule; } + static css::Rule& iconXRule() { return m_iconXRule; } + static css::Rule& iconYRule() { return m_iconYRule; } static css::Rule& textAlignRule() { return m_textAlignRule; } static css::Rule& textColorRule() { return m_textColorRule; } static css::Rule& paddingLeftRule() { return m_paddingLeftRule; } @@ -65,6 +67,8 @@ namespace app { static css::Rule m_backgroundRepeatRule; static css::Rule m_iconAlignRule; static css::Rule m_iconPartRule; + static css::Rule m_iconXRule; + static css::Rule m_iconYRule; static css::Rule m_textAlignRule; static css::Rule m_textColorRule; static css::Rule m_paddingLeftRule; diff --git a/src/app/ui/tabs.cpp b/src/app/ui/tabs.cpp index fc4e08544..f79709fff 100644 --- a/src/app/ui/tabs.cpp +++ b/src/app/ui/tabs.cpp @@ -9,8 +9,6 @@ #include "config.h" #endif -//#define CLOSE_BUTTON_IN_EACH_TAB - #include "app/modules/gfx.h" #include "app/modules/gui.h" #include "app/ui/skin/skin_theme.h" @@ -45,26 +43,6 @@ static WidgetType tabs_type() return type; } -class Tabs::ScrollButton : public Button -{ -public: - ScrollButton(int direction, Tabs* tabs) - : Button("") - , m_direction(direction) - , m_tabs(tabs) { - } - - int getDirection() const { return m_direction; } - -protected: - bool onProcessMessage(Message* msg) override; - void onDisable() override; - -private: - int m_direction; - Tabs* m_tabs; -}; - Tabs::Tabs(TabsDelegate* delegate) : Widget(tabs_type()) , m_delegate(delegate) @@ -73,32 +51,12 @@ Tabs::Tabs(TabsDelegate* delegate) setDoubleBuffered(true); m_hot = NULL; + m_hotCloseButton = false; m_selected = NULL; m_scrollX = 0; m_ani = ANI_NONE; m_removedTab = NULL; - m_button_left = new ScrollButton(-1, this); - m_button_right = new ScrollButton(+1, this); - - setup_mini_look(m_button_left); - setup_mini_look(m_button_right); - setup_bevels(m_button_left, 2, 0, 2, 0); - setup_bevels(m_button_right, 0, 2, 0, 2); - - m_button_left->setFocusStop(false); - m_button_right->setFocusStop(false); - - set_gfxicon_to_button(m_button_left, - PART_COMBOBOX_ARROW_LEFT, - PART_COMBOBOX_ARROW_LEFT_SELECTED, - PART_COMBOBOX_ARROW_LEFT_DISABLED, JI_CENTER | JI_MIDDLE); - - set_gfxicon_to_button(m_button_right, - PART_COMBOBOX_ARROW_RIGHT, - PART_COMBOBOX_ARROW_RIGHT_SELECTED, - PART_COMBOBOX_ARROW_RIGHT_DISABLED, JI_CENTER | JI_MIDDLE); - initTheme(); } @@ -116,15 +74,12 @@ Tabs::~Tabs() for (Tab* tab : m_list) delete tab; m_list.clear(); - - delete m_button_left; // widget - delete m_button_right; // widget } void Tabs::addTab(TabView* tabView) { Tab* tab = new Tab(tabView); - calcTabWidth(tab); + tab->text = tab->view->getTabText(); m_list.push_back(tab); @@ -156,7 +111,6 @@ void Tabs::removeTab(TabView* tabView) it = m_list.erase(it); - // Width of the removed tab if (m_removedTab) { delete m_removedTab; m_removedTab = NULL; @@ -177,10 +131,8 @@ void Tabs::removeTab(TabView* tabView) void Tabs::updateTabsText() { - for (Tab* tab : m_list) { - // Change text of the tab - calcTabWidth(tab); - } + for (Tab* tab : m_list) + tab->text = tab->view->getTabText(); invalidate(); } @@ -258,7 +210,6 @@ bool Tabs::onProcessMessage(Message* msg) return true; case kMouseDownMessage: - case kMouseUpMessage: if (m_hot != NULL) { MouseMessage* mouseMsg = static_cast(msg); @@ -267,13 +218,41 @@ bool Tabs::onProcessMessage(Message* msg) invalidate(); } + if (m_hotCloseButton) { + if (!m_clickedCloseButton) { + m_clickedCloseButton = true; + invalidate(); + } + } + // Left button is processed in mouse down message, right // button is processed in mouse up. if (m_selected && m_delegate && - ((mouseMsg->left() && msg->type() == kMouseDownMessage) || - (!mouseMsg->left() && msg->type() == kMouseUpMessage))) { + !m_clickedCloseButton && + mouseMsg->left()) { m_delegate->clickTab(this, m_selected->view, mouseMsg->buttons()); } + + captureMouse(); + } + return true; + + case kMouseUpMessage: + if (hasCapture()) { + MouseMessage* mouseMsg = static_cast(msg); + + if (m_delegate && m_selected && m_selected == m_hot) { + if (m_hotCloseButton && m_clickedCloseButton) { + m_clickedCloseButton = false; + invalidate(); + + m_delegate->clickClose(this, m_selected->view); + } + else if (!mouseMsg->left()) { + m_delegate->clickTab(this, m_selected->view, mouseMsg->buttons()); + } + } + releaseMouse(); } return true; @@ -303,12 +282,6 @@ bool Tabs::onProcessMessage(Message* msg) case ANI_NONE: // Do nothing break; - case ANI_SCROLL: { - ScrollButton* button = dynamic_cast(getManager()->getCapture()); - if (button != NULL) - setScrollX(m_scrollX + button->getDirection()*8*static_cast(msg)->count()); - break; - } case ANI_SMOOTH_SCROLL: { if (m_ani_t == ANI_SMOOTH_SCROLL_TICKS) { stopAni(); @@ -366,8 +339,9 @@ void Tabs::onPaint(PaintEvent& ev) box.x = box.x2(); // For each tab... + int tabWidth = calcTabWidth(); for (Tab* tab : m_list) { - box.w = tab->width; + box.w = tabWidth; int x_delta = 0; int y_delta = 0; @@ -377,19 +351,19 @@ void Tabs::onPaint(PaintEvent& ev) y_delta = box.h * (ANI_ADDING_TAB_TICKS - m_ani_t) / ANI_ADDING_TAB_TICKS; } else if (m_ani == ANI_REMOVING_TAB && m_nextTabOfTheRemovedOne == tab) { - x_delta += m_removedTab->width - - int(double(m_removedTab->width)*(1.0-std::exp(-10.0 * m_ani_t / (double)ANI_REMOVING_TAB_TICKS))); - x_delta = MID(0, x_delta, m_removedTab->width); + x_delta += tabWidth + - int(double(tabWidth)*(1.0-std::exp(-10.0 * m_ani_t / (double)ANI_REMOVING_TAB_TICKS))); + x_delta = MID(0, x_delta, tabWidth); // Draw deleted tab if (m_removedTab) { gfx::Rect box2(box.x, box.y, x_delta, box.h); - drawTab(g, box2, m_removedTab, 0, false); + drawTab(g, box2, m_removedTab, 0, false, false); } } box.x += x_delta; - drawTab(g, box, tab, y_delta, (tab == m_selected)); + drawTab(g, box, tab, y_delta, (tab == m_hot), (tab == m_selected)); box.x = box.x2(); } @@ -397,12 +371,12 @@ void Tabs::onPaint(PaintEvent& ev) if (m_ani == ANI_REMOVING_TAB && m_nextTabOfTheRemovedOne == NULL) { // Draw deleted tab if (m_removedTab) { - int x_delta = m_removedTab->width - - int(double(m_removedTab->width)*(1.0-std::exp(-10.0 * m_ani_t / (double)ANI_REMOVING_TAB_TICKS))); - x_delta = MID(0, x_delta, m_removedTab->width); + int x_delta = tabWidth + - int(double(tabWidth)*(1.0-std::exp(-10.0 * m_ani_t / (double)ANI_REMOVING_TAB_TICKS))); + x_delta = MID(0, x_delta, tabWidth); gfx::Rect box2(box.x, box.y, x_delta, box.h); - drawTab(g, box2, m_removedTab, 0, false); + drawTab(g, box2, m_removedTab, 0, false, false); box.x += x_delta; box.w = 0; @@ -437,22 +411,6 @@ void Tabs::onPreferredSize(PreferredSizeEvent& ev) ev.setPreferredSize(reqsize); } -void Tabs::onInitTheme(InitThemeEvent& ev) -{ - Widget::onInitTheme(ev); - - SkinTheme* theme = static_cast(ev.getTheme()); - - m_button_left->setBgColor(theme->colors.tabActiveFace()); - m_button_right->setBgColor(theme->colors.tabActiveFace()); -} - -void Tabs::onSetText() -{ - for (Tab* tab : m_list) - calcTabWidth(tab); -} - void Tabs::selectTabInternal(Tab* tab) { m_selected = tab; @@ -460,15 +418,30 @@ void Tabs::selectTabInternal(Tab* tab) invalidate(); } -void Tabs::drawTab(Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, bool selected) +void Tabs::drawTab(Graphics* g, const gfx::Rect& _box, Tab* tab, int y_delta, + bool hover, bool selected) { + gfx::Rect box = _box; + // Is the tab outside the bounds of the widget? if (box.x >= getBounds().x2() || box.x2() <= getBounds().x) return; + if (box.w < ui::guiscale()*8) + box.w = ui::guiscale()*8; + SkinTheme* theme = static_cast(this->getTheme()); gfx::Color text_color; gfx::Color face_color; + int clipTextRightSide; + + gfx::Rect closeBox = getTabCloseButtonBounds(box); + if (closeBox.isEmpty()) + clipTextRightSide = 4*ui::guiscale(); + else { + closeBox.y += y_delta; + clipTextRightSide = closeBox.w; + } // Selected if (selected) { @@ -483,8 +456,14 @@ void Tabs::drawTab(Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, boo skin::Style::State state; if (selected) state += skin::Style::active(); + if (hover) state += skin::Style::hover(); - if (box.w > 2) { + theme->styles.tab()->paint(g, + gfx::Rect(box.x, box.y+y_delta, box.w, box.h), + nullptr, state); + + if (box.w > 8*ui::guiscale()) { + IntersectClip clip(g, gfx::Rect(box.x, box.y+y_delta, box.w-clipTextRightSide, box.h)); theme->styles.tab()->paint(g, gfx::Rect(box.x, box.y+y_delta, box.w, box.h), tab->text.c_str(), state); @@ -494,12 +473,28 @@ void Tabs::drawTab(Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, boo gfx::Rect(box.x, box.y2(), box.w, getBounds().y2()-box.y2()), nullptr, state); -#ifdef CLOSE_BUTTON_IN_EACH_TAB - she::Surface* close_icon = theme->get_part(PART_WINDOW_CLOSE_BUTTON_NORMAL); - g->drawRgbaSurface(close_icon, - box.x2() - 4*guiscale() - close_icon->width(), - box.y + box.h/2 - close_icon->height()/2+1 * guiscale()); -#endif + // Close button + if (!closeBox.isEmpty()) { + skin::Style* style = theme->styles.tabCloseIcon(); + if (m_delegate && + m_delegate->isModified(this, tab->view) && + (!hover || !m_hotCloseButton)) { + style = theme->styles.tabModifiedIcon(); + } + + state = skin::Style::State(); + if (hover && m_hotCloseButton) { + state += skin::Style::hover(); + if (selected) + state += skin::Style::active(); + if (m_clickedCloseButton) + state += skin::Style::clicked(); + } + else if (selected) + state += skin::Style::active(); + + style->paint(g, closeBox, nullptr, state); + } } Tabs::TabsListIterator Tabs::getTabIteratorByView(TabView* tabView) @@ -527,10 +522,11 @@ int Tabs::getMaxScrollX() { TabsListIterator it, end = m_list.end(); int x = 0; + int tabWidth = calcTabWidth(); for (it = m_list.begin(); it != end; ++it) { Tab* tab = *it; - x += tab->width; + x += tabWidth; } x -= getBounds().w; @@ -545,19 +541,20 @@ void Tabs::makeTabVisible(Tab* make_visible_this_tab) { int x = 0; int extra_x = getMaxScrollX() > 0 ? ARROW_W*2: 0; + int tabWidth = calcTabWidth(); for (Tab* tab : m_list) { if (tab == make_visible_this_tab) { if (x - m_scrollX < 0) { setScrollX(x); } - else if (x + tab->width - m_scrollX > getBounds().w - extra_x) { - setScrollX(x + tab->width - getBounds().w + extra_x); + else if (x + tabWidth - m_scrollX > getBounds().w - extra_x) { + setScrollX(x + tabWidth - getBounds().w + extra_x); } break; } - x += tab->width; + x += tabWidth; } } @@ -571,56 +568,35 @@ void Tabs::setScrollX(int scroll_x) calculateHot(); invalidate(); } - - // We need scroll buttons? - if (max_x > 0) { - // Add childs - if (!HAS_ARROWS(this)) { - addChild(m_button_left); - addChild(m_button_right); - invalidate(); - } - - /* disable/enable buttons */ - m_button_left->setEnabled(m_scrollX > 0); - m_button_right->setEnabled(m_scrollX < max_x); - - // Setup the position of each button - gfx::Rect rect = getBounds(); - gfx::Rect box(rect.x2()-ARROW_W*2, rect.y, ARROW_W, rect.h-2); - m_button_left->setBounds(box); - - box.x += ARROW_W; - m_button_right->setBounds(box); - } - // Remove buttons - else if (HAS_ARROWS(this)) { - removeChild(m_button_left); - removeChild(m_button_right); - invalidate(); - } } void Tabs::calculateHot() { + SkinTheme* theme = static_cast(this->getTheme()); gfx::Rect rect = getBounds(); gfx::Rect box(rect.x-m_scrollX, rect.y, 0, rect.h-1); - Tab *hot = NULL; + gfx::Point mousePos = ui::get_mouse_position(); + Tab* hot = NULL; + bool hotCloseButton = false; + int tabWidth = calcTabWidth(); // For each tab for (Tab* tab : m_list) { - box.w = tab->width; + box.w = tabWidth; - if (box.contains(ui::get_mouse_position())) { + if (box.contains(mousePos)) { hot = tab; + hotCloseButton = getTabCloseButtonBounds(box).contains(mousePos); break; } box.x += box.w; } - if (m_hot != hot) { + if (m_hot != hot || + m_hotCloseButton != hotCloseButton) { m_hot = hot; + m_hotCloseButton = hotCloseButton; if (m_delegate) m_delegate->mouseOverTab(this, m_hot ? m_hot->view: NULL); @@ -629,29 +605,29 @@ void Tabs::calculateHot() } } -void Tabs::calcTabWidth(Tab* tab) +int Tabs::calcTabWidth() { - // Cache current tab text - tab->text = tab->view->getTabText(); + SkinTheme* theme = static_cast(this->getTheme()); + int tabWidth = theme->dimensions.tabsWidth(); - int border = 4*guiscale(); -#ifdef CLOSE_BUTTON_IN_EACH_TAB - SkinTheme* theme = static_cast(getTheme()); - int close_icon_w = theme->get_part(PART_WINDOW_CLOSE_BUTTON_NORMAL)->width(); - tab->width = (border + getFont()->textLength(tab->text.c_str()) + border + close_icon_w + border); -#else - tab->width = (border + getFont()->textLength(tab->text.c_str()) + border); -#endif + if (tabWidth * m_list.size() > (size_t)getBounds().w) { + tabWidth = getBounds().w / m_list.size(); + tabWidth = MAX(2*ui::guiscale(), tabWidth); + } + + return tabWidth; } -void Tabs::startScrolling() +gfx::Rect Tabs::getTabCloseButtonBounds(const gfx::Rect& box) { - startAni(ANI_SCROLL); -} + SkinTheme* theme = static_cast(this->getTheme()); + int iconW = theme->dimensions.tabsCloseIconWidth(); + int iconH = theme->dimensions.tabsCloseIconHeight(); -void Tabs::stopScrolling() -{ - stopAni(); + if (box.w-iconW > 4*ui::guiscale()) + return gfx::Rect(box.x2()-iconW, box.y+box.h/2-iconH/2, iconW, iconH); + else + return gfx::Rect(); } void Tabs::startAni(Ani ani) @@ -671,38 +647,4 @@ void Tabs::stopAni() m_timer.stop(); } -bool Tabs::ScrollButton::onProcessMessage(Message* msg) -{ - switch (msg->type()) { - - case kMouseDownMessage: - captureMouse(); - m_tabs->startScrolling(); - return true; - - case kMouseUpMessage: - if (hasCapture()) - releaseMouse(); - - m_tabs->stopScrolling(); - return true; - - } - - return Button::onProcessMessage(msg); -} - -void Tabs::ScrollButton::onDisable() -{ - Button::onDisable(); - - if (hasCapture()) - releaseMouse(); - - if (isSelected()) { - m_tabs->stopScrolling(); - setSelected(false); - } -} - } // namespace app diff --git a/src/app/ui/tabs.h b/src/app/ui/tabs.h index 3d80ecdbc..9be8f0695 100644 --- a/src/app/ui/tabs.h +++ b/src/app/ui/tabs.h @@ -40,9 +40,14 @@ namespace app { // Called when the user presses a mouse button over a tab. virtual void clickTab(Tabs* tabs, TabView* tabView, ui::MouseButtons buttons) = 0; + // When the tab close button is pressed. + virtual void clickClose(Tabs* tabs, TabView* tabView) = 0; + // Called when the mouse is over a tab (the data can be null if the // mouse just leave all tabs) virtual void mouseOverTab(Tabs* tabs, TabView* tabView) = 0; + + virtual bool isModified(Tabs* tabs, TabView* tabView) = 0; }; // Tabs control. Used to show opened documents. @@ -50,9 +55,8 @@ namespace app { struct Tab { TabView* view; std::string text; - int width; - Tab(TabView* view) : view(view), width(0) { + Tab(TabView* view) : view(view) { } }; @@ -62,7 +66,6 @@ namespace app { enum Ani { ANI_NONE, ANI_ADDING_TAB, ANI_REMOVING_TAB, - ANI_SCROLL, ANI_SMOOTH_SCROLL }; public: @@ -78,33 +81,31 @@ namespace app { void selectPreviousTab(); TabView* getSelectedTab(); - void startScrolling(); - void stopScrolling(); - protected: bool onProcessMessage(ui::Message* msg) override; void onPaint(ui::PaintEvent& ev) override; void onResize(ui::ResizeEvent& ev) override; void onPreferredSize(ui::PreferredSizeEvent& ev) override; - void onInitTheme(ui::InitThemeEvent& ev) override; - void onSetText() override; private: void startAni(Ani ani); void stopAni(); void selectTabInternal(Tab* tab); - void drawTab(ui::Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, bool selected); + void drawTab(ui::Graphics* g, const gfx::Rect& box, Tab* tab, int y_delta, bool hover, bool selected); TabsListIterator getTabIteratorByView(TabView* tabView); Tab* getTabByView(TabView* tabView); int getMaxScrollX(); void makeTabVisible(Tab* tab); void setScrollX(int scroll_x); void calculateHot(); - void calcTabWidth(Tab* tab); + int calcTabWidth(); + gfx::Rect getTabCloseButtonBounds(const gfx::Rect& box); TabsList m_list; Tab* m_hot; + bool m_hotCloseButton; + bool m_clickedCloseButton; Tab* m_selected; int m_scrollX; @@ -119,11 +120,6 @@ namespace app { int m_ani_t; // Number of ticks from the beginning of the animation Tab* m_removedTab; Tab* m_nextTabOfTheRemovedOne; - - // Buttons to scroll tabs (useful when there are more tabs than visible area) - class ScrollButton; - ScrollButton* m_button_left; - ScrollButton* m_button_right; }; } // namespace app