From a62f12ad0900c9ef6fce5826d96778deb87c8a30 Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 17 Feb 2017 14:18:47 -0300 Subject: [PATCH] Move all code to draw windows to ui-lib w/new styles --- data/themes/default/theme.xml | 80 +++++++-- src/app/ui/color_popup.cpp | 9 +- src/app/ui/popup_window_pin.cpp | 3 +- src/app/ui/popup_window_pin.h | 3 +- src/app/ui/preview_editor.cpp | 4 +- src/app/ui/skin/skin_theme.cpp | 297 ++++++-------------------------- src/app/ui/skin/skin_theme.h | 6 - src/app/ui/toolbar.cpp | 5 +- src/ui/int_entry.cpp | 29 ++-- src/ui/popup_window.cpp | 64 ++----- src/ui/popup_window.h | 10 +- src/ui/style.cpp | 11 +- src/ui/style.h | 5 + src/ui/theme.cpp | 220 ++++++++++++++++++++--- src/ui/theme.h | 23 ++- src/ui/tooltips.cpp | 64 +++---- src/ui/tooltips.h | 13 +- src/ui/ui.h | 1 + src/ui/widget.cpp | 9 +- src/ui/widget_type.h | 2 + src/ui/window.cpp | 147 ++++++++++++---- src/ui/window.h | 11 +- 22 files changed, 544 insertions(+), 472 deletions(-) diff --git a/data/themes/default/theme.xml b/data/themes/default/theme.xml index 1b3cfd27d..4a7b83412 100644 --- a/data/themes/default/theme.xml +++ b/data/themes/default/theme.xml @@ -412,24 +412,6 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/src/app/ui/color_popup.cpp b/src/app/ui/color_popup.cpp index 493decd9b..fdd5db9ea 100644 --- a/src/app/ui/color_popup.cpp +++ b/src/app/ui/color_popup.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -80,12 +80,13 @@ ColorPopup::ColorPopup(bool canPin) m_topBox.addChild(new Separator("", VERTICAL)); m_topBox.addChild(&m_hexColorEntry); + // TODO fix this hack for close button in popup window // Move close button (decorative widget) inside the m_topBox { Widget* closeButton = nullptr; WidgetsList decorators; for (auto child : children()) { - if (child->isDecorative()) { + if (child->type() == kWindowCloseButtonWidget) { closeButton = child; removeChild(child); break; @@ -93,7 +94,9 @@ ColorPopup::ColorPopup(bool canPin) } if (closeButton) { m_topBox.addChild(new BoxFiller); - m_topBox.addChild(closeButton); + VBox* vbox = new VBox; + vbox->addChild(closeButton); + m_topBox.addChild(vbox); } } diff --git a/src/app/ui/popup_window_pin.cpp b/src/app/ui/popup_window_pin.cpp index 2618a5237..4d140c920 100644 --- a/src/app/ui/popup_window_pin.cpp +++ b/src/app/ui/popup_window_pin.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -12,7 +12,6 @@ #include "app/modules/gfx.h" #include "app/modules/gui.h" -#include "app/ui/skin/button_icon_impl.h" #include "app/ui/skin/skin_theme.h" #include "base/bind.h" #include "gfx/border.h" diff --git a/src/app/ui/popup_window_pin.h b/src/app/ui/popup_window_pin.h index 2c241b06a..a75ce5c76 100644 --- a/src/app/ui/popup_window_pin.h +++ b/src/app/ui/popup_window_pin.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -8,7 +8,6 @@ #define APP_UI_POPUP_FRAME_PIN_H_INCLUDED #pragma once -#include "ui/button.h" #include "ui/popup_window.h" namespace app { diff --git a/src/app/ui/preview_editor.cpp b/src/app/ui/preview_editor.cpp index 92d1bf7e7..658e9dff2 100644 --- a/src/app/ui/preview_editor.cpp +++ b/src/app/ui/preview_editor.cpp @@ -241,8 +241,8 @@ bool PreviewEditorWindow::onProcessMessage(ui::Message* msg) void PreviewEditorWindow::onClose(ui::CloseEvent& ev) { Button* closeButton = dynamic_cast(ev.getSource()); - if (closeButton != NULL && - closeButton->id() == SkinTheme::kThemeCloseButtonId) { + if (closeButton && + closeButton->type() == kWindowCloseButtonWidget) { // Here we don't use "setMiniEditorEnabled" to change the state of // "m_isEnabled" because we're coming from a close event of the // window. diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp index e0520c335..141eed3a4 100644 --- a/src/app/ui/skin/skin_theme.cpp +++ b/src/app/ui/skin/skin_theme.cpp @@ -50,61 +50,6 @@ using namespace gfx; using namespace ui; const char* SkinTheme::kThemesFolderName = "themes"; -const char* SkinTheme::kThemeCloseButtonId = "theme_close_button"; - -// Controls the "X" button in a window to close it. -class WindowCloseButton : public Button { -public: - WindowCloseButton() : Button("") { - setDecorative(true); - setId(SkinTheme::kThemeCloseButtonId); - } - -protected: - - void onSizeHint(SizeHintEvent& ev) override { - ev.setSizeHint(SkinTheme::instance()->parts.windowButtonNormal()->size()); - } - - void onClick(Event& ev) override { - Button::onClick(ev); - closeWindow(); - } - - void onPaint(PaintEvent& ev) override { - static_cast(theme())->paintWindowButton(ev); - } - - bool onProcessMessage(Message* msg) override { - switch (msg->type()) { - - case kSetCursorMessage: - ui::set_mouse_cursor(kArrowCursor); - return true; - - case kKeyDownMessage: - if (window()->isForeground() && - static_cast(msg)->scancode() == kKeyEsc) { - setSelected(true); - return true; - } - break; - - case kKeyUpMessage: - if (window()->isForeground() && - static_cast(msg)->scancode() == kKeyEsc) { - if (isSelected()) { - setSelected(false); - closeWindow(); - return true; - } - } - break; - } - - return Button::onProcessMessage(msg); - } -}; static const char* cursor_names[kCursorTypes] = { "null", // kNoCursor @@ -534,6 +479,21 @@ void SkinTheme::loadXml(const std::string& skinId) *style = ui::Style(base); } + // Margin + { + const char* m = xmlStyle->Attribute("margin"); + const char* l = xmlStyle->Attribute("margin-left"); + const char* t = xmlStyle->Attribute("margin-top"); + const char* r = xmlStyle->Attribute("margin-right"); + const char* b = xmlStyle->Attribute("margin-bottom"); + gfx::Border margin = ui::Style::UndefinedBorder(); + if (m || l) margin.left(std::strtol(l ? l: m, nullptr, 10)); + if (m || t) margin.top(std::strtol(t ? t: m, nullptr, 10)); + if (m || r) margin.right(std::strtol(r ? r: m, nullptr, 10)); + if (m || b) margin.bottom(std::strtol(b ? b: m, nullptr, 10)); + style->setMargin(margin*guiscale()); + } + // Border { const char* m = xmlStyle->Attribute("border"); @@ -541,7 +501,7 @@ void SkinTheme::loadXml(const std::string& skinId) const char* t = xmlStyle->Attribute("border-top"); const char* r = xmlStyle->Attribute("border-right"); const char* b = xmlStyle->Attribute("border-bottom"); - gfx::Border border(-1, -1, -1, -1); + gfx::Border border = ui::Style::UndefinedBorder(); if (m || l) border.left(std::strtol(l ? l: m, nullptr, 10)); if (m || t) border.top(std::strtol(t ? t: m, nullptr, 10)); if (m || r) border.right(std::strtol(r ? r: m, nullptr, 10)); @@ -556,7 +516,7 @@ void SkinTheme::loadXml(const std::string& skinId) const char* t = xmlStyle->Attribute("padding-top"); const char* r = xmlStyle->Attribute("padding-right"); const char* b = xmlStyle->Attribute("padding-bottom"); - gfx::Border padding(-1, -1, -1, -1); + gfx::Border padding = ui::Style::UndefinedBorder(); if (m || l) padding.left(std::strtol(l ? l: m, nullptr, 10)); if (m || t) padding.top(std::strtol(t ? t: m, nullptr, 10)); if (m || r) padding.right(std::strtol(r ? r: m, nullptr, 10)); @@ -778,10 +738,15 @@ void SkinTheme::initWidget(Widget* widget) } case kMenuWidget: + widget->setStyle(newStyles.menu()); + break; + case kMenuBarWidget: + widget->setStyle(newStyles.menubar()); + break; + case kMenuBoxWidget: - BORDER(0); - widget->setChildSpacing(0); + widget->setStyle(newStyles.menubox()); break; case kMenuItemWidget: @@ -854,33 +819,36 @@ void SkinTheme::initWidget(Widget* widget) break; case kWindowWidget: - if (!static_cast(widget)->isDesktop()) { - if (widget->hasText()) { - BORDER4(6 * scale, - (4+6) * scale + widget->textHeight(), - 6 * scale, - 6 * scale); - - if (!widget->hasFlags(INITIALIZED)) { - Button* button = new WindowCloseButton(); - widget->addChild(button); - } - } - else { - BORDER(3 * scale); - } + if (TipWindow* window = dynamic_cast(widget)) { + window->setStyle(newStyles.tooltipWindow()); + window->setArrowStyle(newStyles.tooltipWindowArrow()); + window->textBox()->setStyle(SkinTheme::instance()->newStyles.tooltipText()); + } + else if (dynamic_cast(widget)) { + widget->setStyle(newStyles.transparentPopupWindow()); + } + else if (dynamic_cast(widget)) { + widget->setStyle(newStyles.popupWindow()); + } + else if (static_cast(widget)->isDesktop()) { + widget->setStyle(newStyles.desktop()); } else { - BORDER(0); + if (widget->hasText()) { + widget->setStyle(newStyles.windowWithTitle()); + } + else { + widget->setStyle(newStyles.windowWithoutTitle()); + } } + break; - widget->setChildSpacing(4 * scale); // TODO this hard-coded 4 should be configurable in skin.xml + case kWindowTitleLabelWidget: + widget->setStyle(SkinTheme::instance()->newStyles.windowTitleLabel()); + break; - // Tooltip background color - if (dynamic_cast(widget)) - widget->setBgColor(SkinTheme::instance()->colors.tooltipFace()); - else - widget->setBgColor(colors.windowFace()); + case kWindowCloseButtonWidget: + widget->setStyle(SkinTheme::instance()->newStyles.windowCloseButton()); break; default: @@ -893,19 +861,6 @@ void SkinTheme::getWindowMask(Widget* widget, Region& region) region = widget->bounds(); } -void SkinTheme::setDecorativeWidgetBounds(Widget* widget) -{ - if (widget->id() == kThemeCloseButtonId) { - Widget* window = widget->parent(); - gfx::Rect rect(parts.windowButtonNormal()->size()); - - rect.offset(window->bounds().x2() - 3*guiscale() - rect.w, - window->bounds().y + 3*guiscale()); - - widget->setBounds(rect); - } -} - int SkinTheme::getScrollbarSize() { return dimensions.scrollbarSize(); @@ -1603,160 +1558,6 @@ void SkinTheme::paintViewViewport(PaintEvent& ev) g->fillRect(bg, widget->clientBounds()); } -void SkinTheme::paintWindow(PaintEvent& ev) -{ - Graphics* g = ev.graphics(); - Window* window = static_cast(ev.getSource()); - Rect pos = window->clientBounds(); - Rect cpos = window->clientChildrenBounds(); - - if (!window->isDesktop()) { - // window frame - if (window->hasText()) { - styles.window()->paint(g, pos, NULL, Style::State()); - styles.windowTitle()->paint(g, - gfx::Rect(cpos.x, pos.y+5*guiscale(), cpos.w, // TODO this hard-coded 5 should be configurable in skin.xml - window->textHeight()), - window->text().c_str(), Style::State()); - } - // menubox - else { - styles.menubox()->paint(g, pos, NULL, Style::State()); - } - } - // desktop - else { - styles.desktop()->paint(g, pos, NULL, Style::State()); - } -} - -void SkinTheme::paintPopupWindow(PaintEvent& ev) -{ - Widget* widget = static_cast(ev.getSource()); - Window* window = static_cast(ev.getSource()); - Graphics* g = ev.graphics(); - gfx::Rect pos = window->clientBounds(); - - if (!is_transparent(BGCOLOR)) - styles.menubox()->paint(g, pos, NULL, Style::State()); - - pos.shrink(window->border()); - - g->drawAlignedUIText(window->text(), - colors.text(), - window->bgColor(), pos, - window->align()); -} - -void SkinTheme::paintWindowButton(ui::PaintEvent& ev) -{ - // Merge this code with SkinButton class - - ButtonBase* widget = static_cast(ev.getSource()); - Graphics* g = ev.graphics(); - Rect rc = widget->clientBounds(); - SkinPartPtr part; - gfx::Color fg; - - if (widget->isSelected()) { - fg = colors.buttonSelectedText(); - part = parts.windowButtonSelected(); - } - else if (widget->hasMouseOver()) { - fg = colors.buttonHotText(); - part = parts.windowButtonHot(); - } - else { - fg = colors.buttonNormalText(); - part = parts.windowButtonNormal(); - } - - g->fillRect(BGCOLOR, rc); - g->drawRgbaSurface(part->bitmap(0), rc.x, rc.y); - gfx::Size sz(part->bitmap(0)->width(), - part->bitmap(0)->height()); - - part = parts.windowCloseIcon(); - g->drawColoredRgbaSurface(part->bitmap(0), fg, - rc.x+sz.w/2-part->bitmap(0)->width()/2, - rc.y+sz.h/2-part->bitmap(0)->height()/2); -} - -void SkinTheme::paintTooltip(PaintEvent& ev) -{ - ui::TipWindow* widget = static_cast(ev.getSource()); - Graphics* g = ev.graphics(); - Rect absRc = widget->bounds(); - Rect rc = widget->clientBounds(); - gfx::Color fg = colors.tooltipText(); - gfx::Color bg = colors.tooltipFace(); - SkinPartPtr tooltipPart = parts.tooltip(); - - she::Surface* nw = tooltipPart->bitmapNW(); - she::Surface* n = tooltipPart->bitmapN(); - she::Surface* ne = tooltipPart->bitmapNE(); - she::Surface* e = tooltipPart->bitmapE(); - she::Surface* se = tooltipPart->bitmapSE(); - she::Surface* s = tooltipPart->bitmapS(); - she::Surface* sw = tooltipPart->bitmapSW(); - she::Surface* w = tooltipPart->bitmapW(); - - switch (widget->arrowAlign()) { - case TOP | LEFT: nw = parts.tooltipArrow()->bitmapNW(); break; - case TOP | RIGHT: ne = parts.tooltipArrow()->bitmapNE(); break; - case BOTTOM | LEFT: sw = parts.tooltipArrow()->bitmapSW(); break; - case BOTTOM | RIGHT: se = parts.tooltipArrow()->bitmapSE(); break; - } - - drawRect(g, rc, nw, n, ne, e, se, s, sw, w); - - // Draw arrow in sides - she::Surface* arrow = NULL; - gfx::Rect target(widget->target()); - target = target.createIntersection(gfx::Rect(0, 0, ui::display_w(), ui::display_h())); - target.offset(-absRc.origin()); - - switch (widget->arrowAlign()) { - case TOP: - arrow = parts.tooltipArrow()->bitmapN(); - g->drawRgbaSurface(arrow, - target.x+target.w/2-arrow->width()/2, - rc.y); - break; - case BOTTOM: - arrow = parts.tooltipArrow()->bitmapS(); - g->drawRgbaSurface(arrow, - target.x+target.w/2-arrow->width()/2, - rc.y+rc.h-arrow->height()); - break; - case LEFT: - arrow = parts.tooltipArrow()->bitmapW(); - g->drawRgbaSurface(arrow, - rc.x, - target.y+target.h/2-arrow->height()/2); - break; - case RIGHT: - arrow = parts.tooltipArrow()->bitmapE(); - g->drawRgbaSurface(arrow, - rc.x+rc.w-arrow->width(), - target.y+target.h/2-arrow->height()/2); - break; - } - - // Fill background - g->fillRect( - bg, Rect(rc).shrink( - Border( - w->width(), - n->height(), - e->width(), - s->height()))); - - rc.shrink(widget->border()); - - g->drawAlignedUIText(widget->text(), fg, bg, rc, widget->align()); -} - gfx::Color SkinTheme::getWidgetBgColor(Widget* widget) { gfx::Color c = widget->bgColor(); diff --git a/src/app/ui/skin/skin_theme.h b/src/app/ui/skin/skin_theme.h index e9496b2c4..3626352dc 100644 --- a/src/app/ui/skin/skin_theme.h +++ b/src/app/ui/skin/skin_theme.h @@ -39,7 +39,6 @@ namespace app { , public app::gen::ThemeFile { public: static const char* kThemesFolderName; - static const char* kThemeCloseButtonId; static SkinTheme* instance(); @@ -53,7 +52,6 @@ namespace app { ui::Cursor* getCursor(ui::CursorType type) override; void initWidget(ui::Widget* widget) override; void getWindowMask(ui::Widget* widget, gfx::Region& region) override; - void setDecorativeWidgetBounds(ui::Widget* widget) override; int getScrollbarSize() override; gfx::Size getEntryCaretSize(ui::Widget* widget) override; @@ -74,10 +72,6 @@ namespace app { void paintTextBox(ui::PaintEvent& ev) override; void paintViewScrollbar(ui::PaintEvent& ev) override; void paintViewViewport(ui::PaintEvent& ev) override; - void paintWindow(ui::PaintEvent& ev) override; - void paintPopupWindow(ui::PaintEvent& ev) override; - void paintTooltip(ui::PaintEvent& ev) override; - void paintWindowButton(ui::PaintEvent& ev); int get_button_selected_offset() const { return 0; } // TODO Configurable in xml diff --git a/src/app/ui/toolbar.cpp b/src/app/ui/toolbar.cpp index 0a4077a8d..310215d5e 100644 --- a/src/app/ui/toolbar.cpp +++ b/src/app/ui/toolbar.cpp @@ -401,7 +401,7 @@ void ToolBar::openPopupWindow(int group_index, ToolGroup* tool_group) return; // In case this tool contains more than just one tool, show the popup window - m_popupWindow = new PopupWindow("", PopupWindow::ClickBehavior::CloseOnClickOutsideHotRegion); + m_popupWindow = new TransparentPopupWindow(PopupWindow::ClickBehavior::CloseOnClickOutsideHotRegion); m_closeConn = m_popupWindow->Close.connect(base::Bind(&ToolBar::onClosePopup, this)); m_openedRecently = true; @@ -425,9 +425,6 @@ void ToolBar::openPopupWindow(int group_index, ToolGroup* tool_group) Region rgn(gfx::Rect(rc).enlarge(16*guiscale())); rgn.createUnion(rgn, Region(bounds())); m_popupWindow->setHotRegion(rgn); - - m_popupWindow->setTransparent(true); - m_popupWindow->setBgColor(gfx::ColorNone); m_popupWindow->setAutoRemap(false); m_popupWindow->setBounds(rc); toolstrip->setBounds(rc); diff --git a/src/ui/int_entry.cpp b/src/ui/int_entry.cpp index 36297a04a..47b13bb85 100644 --- a/src/ui/int_entry.cpp +++ b/src/ui/int_entry.cpp @@ -33,7 +33,7 @@ IntEntry::IntEntry(int min, int max, SliderDelegate* sliderDelegate) , m_min(min) , m_max(max) , m_slider(m_min, m_max, m_min, sliderDelegate) - , m_popupWindow(NULL) + , m_popupWindow(nullptr) , m_changeFromSlider(false) { m_slider.setFocusStop(false); // In this way the IntEntry doesn't lost the focus @@ -160,31 +160,26 @@ void IntEntry::openPopup() { m_slider.setValue(getValue()); + m_popupWindow = new TransparentPopupWindow(PopupWindow::ClickBehavior::CloseOnClickInOtherWindow); + m_popupWindow->setAutoRemap(false); + m_popupWindow->addChild(&m_slider); + m_popupWindow->Close.connect(&IntEntry::onPopupClose, this); + Rect rc = bounds(); - int sliderH = m_slider.sizeHint().h; - - if (rc.y+rc.h+sliderH < ui::display_h()) - rc.y += rc.h; - else - rc.y -= sliderH; - - rc.h = sliderH; + gfx::Size sz = m_popupWindow->sizeHint(); rc.w = 128*guiscale(); if (rc.x+rc.w > ui::display_w()) - rc.x = rc.x - rc.w + bounds().w; - - m_popupWindow = new PopupWindow("", PopupWindow::ClickBehavior::CloseOnClickInOtherWindow); - m_popupWindow->setAutoRemap(false); - m_popupWindow->setTransparent(true); - m_popupWindow->setBgColor(gfx::ColorNone); + rc.x = rc.x-rc.w+bounds().w; + if (rc.y+rc.h+sz.h < ui::display_h()) + rc.y += rc.h; + else + rc.y -= sz.h; m_popupWindow->setBounds(rc); - m_popupWindow->Close.connect(&IntEntry::onPopupClose, this); Region rgn(rc.createUnion(bounds())); rgn.createUnion(rgn, Region(bounds())); m_popupWindow->setHotRegion(rgn); - m_popupWindow->addChild(&m_slider); m_popupWindow->openWindow(); } diff --git a/src/ui/popup_window.cpp b/src/ui/popup_window.cpp index 8e1204ba2..6f969e8a0 100644 --- a/src/ui/popup_window.cpp +++ b/src/ui/popup_window.cpp @@ -34,11 +34,17 @@ PopupWindow::PopupWindow(const std::string& text, setWantFocus(false); setAlign(LEFT | TOP); - if (!withCloseButton) - removeDecorativeWidgets(); + if (!withCloseButton) { + // Remove close button + for (auto child : children()) { + if (child->type() == kWindowCloseButtonWidget) { + delete child; + break; + } + } + } initTheme(); - noBorderNoChildSpacing(); } PopupWindow::~PopupWindow() @@ -172,50 +178,6 @@ bool PopupWindow::onProcessMessage(Message* msg) return Window::onProcessMessage(msg); } -void PopupWindow::onSizeHint(SizeHintEvent& ev) -{ - ScreenGraphics g; - g.setFont(font()); - Size resultSize(0, 0); - - if (hasText()) - resultSize = g.fitString(text(), - (clientBounds() - border()).w, - align()); - - resultSize.w += border().width(); - resultSize.h += border().height(); - - if (!children().empty()) { - Size maxSize(0, 0); - Size reqSize; - - for (auto child : children()) { - reqSize = child->sizeHint(); - - maxSize.w = MAX(maxSize.w, reqSize.w); - maxSize.h = MAX(maxSize.h, reqSize.h); - } - - resultSize.w = MAX(resultSize.w, maxSize.w + border().width()); - resultSize.h += maxSize.h; - } - - ev.setSizeHint(resultSize); -} - -void PopupWindow::onPaint(PaintEvent& ev) -{ - theme()->paintPopupWindow(ev); -} - -void PopupWindow::onInitTheme(InitThemeEvent& ev) -{ - Widget::onInitTheme(ev); - - setBorder(gfx::Border(3 * guiscale())); -} - void PopupWindow::onHitTest(HitTestEvent& ev) { Window::onHitTest(ev); @@ -283,4 +245,12 @@ void PopupWindow::onMakeFixed() // Do nothing } +TransparentPopupWindow::TransparentPopupWindow(ClickBehavior clickBehavior) + : PopupWindow("", clickBehavior) +{ + setTransparent(true); + setBgColor(gfx::ColorNone); + initTheme(); +} + } // namespace ui diff --git a/src/ui/popup_window.h b/src/ui/popup_window.h index a8fc2477c..86828ee06 100644 --- a/src/ui/popup_window.h +++ b/src/ui/popup_window.h @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -42,9 +42,6 @@ namespace ui { protected: bool onProcessMessage(Message* msg) override; - void onSizeHint(SizeHintEvent& ev) override; - void onPaint(PaintEvent& ev) override; - void onInitTheme(InitThemeEvent& ev) override; void onHitTest(HitTestEvent& ev) override; virtual void onMakeFloating(); @@ -61,6 +58,11 @@ namespace ui { bool m_fixed; }; + class TransparentPopupWindow : public PopupWindow { + public: + TransparentPopupWindow(ClickBehavior clickBehavior); + }; + } // namespace ui #endif diff --git a/src/ui/style.cpp b/src/ui/style.cpp index a1815daff..767080aa4 100644 --- a/src/ui/style.cpp +++ b/src/ui/style.cpp @@ -12,10 +12,17 @@ namespace ui { +// static +gfx::Border Style::UndefinedBorder() +{ + return gfx::Border(-1, -1, -1, -1); +} + Style::Style(const Style* base) : m_insertionPoint(0) - , m_border(base ? base->border(): gfx::Border(-1, -1, -1, -1)) - , m_padding(base ? base->padding(): gfx::Border(-1, -1, -1, -1)) + , m_margin(base ? base->margin(): Style::UndefinedBorder()) + , m_border(base ? base->border(): Style::UndefinedBorder()) + , m_padding(base ? base->padding(): Style::UndefinedBorder()) { if (base) m_layers = base->layers(); diff --git a/src/ui/style.h b/src/ui/style.h index db65fcb8e..c2f9f9611 100644 --- a/src/ui/style.h +++ b/src/ui/style.h @@ -85,14 +85,18 @@ namespace ui { typedef std::vector Layers; + static gfx::Border UndefinedBorder(); + Style(const Style* base); const std::string& id() const { return m_id; } + const gfx::Border& margin() const { return m_margin; } const gfx::Border& border() const { return m_border; } const gfx::Border& padding() const { return m_padding; } const Layers& layers() const { return m_layers; } void setId(const std::string& id) { m_id = id; } + void setMargin(const gfx::Border& value) { m_margin = value; } void setBorder(const gfx::Border& value) { m_border = value; } void setPadding(const gfx::Border& value) { m_padding = value; } void addLayer(const Layer& layer); @@ -101,6 +105,7 @@ namespace ui { std::string m_id; // Just for debugging purposes Layers m_layers; int m_insertionPoint; + gfx::Border m_margin; gfx::Border m_border; gfx::Border m_padding; }; diff --git a/src/ui/theme.cpp b/src/ui/theme.cpp index 80e8adcad..3075b286c 100644 --- a/src/ui/theme.cpp +++ b/src/ui/theme.cpp @@ -23,6 +23,7 @@ #include "ui/system.h" #include "ui/view.h" #include "ui/widget.h" +#include "ui/window.h" #include #include @@ -101,18 +102,58 @@ void Theme::regenerate() set_mouse_cursor(type); } +void Theme::setDecorativeWidgetBounds(Widget* widget) +{ + switch (widget->type()) { + + case kWindowTitleLabelWidget: { + Window* window = widget->window(); + gfx::Rect labelBounds(widget->sizeHint()); + gfx::Rect windowBounds(window->bounds()); + gfx::Border margin; + if (widget->style()) + margin = widget->style()->margin(); + + labelBounds.offset( + windowBounds.x + margin.left(), + windowBounds.y + margin.top()); + + widget->setBounds(labelBounds); + break; + } + + case kWindowCloseButtonWidget: { + Window* window = widget->window(); + gfx::Rect buttonBounds(widget->sizeHint()); + gfx::Rect windowBounds(window->bounds()); + gfx::Border margin(0, 0, 0, 0); + if (widget->style()) + margin = widget->style()->margin(); + + buttonBounds.offset( + windowBounds.x2() - margin.right() - buttonBounds.w, + windowBounds.y + margin.top()); + + widget->setBounds(buttonBounds); + break; + } + + } +} + void Theme::paintWidget(Graphics* g, const Widget* widget, const Style* style, - gfx::Rect rc) + const gfx::Rect& bounds) { ASSERT(g); ASSERT(widget); ASSERT(style); // External background - g->fillRect(widget->bgColor(), rc); + g->fillRect(widget->bgColor(), bounds); + gfx::Rect rc = bounds; for_each_layer( widget, style, [this, g, widget, &rc](const Style::Layer& layer) { @@ -120,6 +161,68 @@ void Theme::paintWidget(Graphics* g, }); } +void Theme::paintTooltip(Graphics* g, + const Widget* widget, + const Style* style, + const Style* arrowStyle, + const gfx::Rect& bounds, + const int arrowAlign, + const gfx::Rect& target) +{ + if (style) + paintWidget(g, widget, style, bounds); + + // Draw arrow + if (arrowStyle) { + gfx::Size topLeft; + gfx::Size center; + gfx::Size bottomRight; + calcSlices(widget, arrowStyle, + topLeft, center, bottomRight); + + gfx::Rect clip, rc(0, 0, + topLeft.w+center.w+bottomRight.w, + topLeft.h+center.h+bottomRight.h); + + if (arrowAlign & LEFT) { + clip.w = topLeft.w; + clip.x = bounds.x; + rc.x = bounds.x; + } + else if (arrowAlign & RIGHT) { + clip.w = bottomRight.w; + clip.x = bounds.x+bounds.w-clip.w; + rc.x = bounds.x2()-rc.w; + } + else { + clip.w = center.w; + clip.x = target.x+target.w/2-clip.w/2; + rc.x = clip.x - topLeft.w; + } + + if (arrowAlign & TOP) { + clip.h = topLeft.h; + clip.y = bounds.y; + rc.y = bounds.y; + } + else if (arrowAlign & BOTTOM) { + clip.h = bottomRight.h; + clip.y = bounds.y+bounds.h-clip.h; + rc.y = bounds.y2()-rc.h; + } + else { + clip.h = center.h; + clip.y = target.y+target.h/2-clip.h/2; + rc.y = clip.y - topLeft.h; + } + + IntersectClip intClip(g, clip); + if (intClip) + paintWidget(g, widget, arrowStyle, rc); + } + +} + void Theme::paintLayer(Graphics* g, const Widget* widget, const Style::Layer& layer, @@ -133,16 +236,26 @@ void Theme::paintLayer(Graphics* g, } if (layer.spriteSheet() && - !layer.spriteBounds().isEmpty() && - !layer.slicesBounds().isEmpty()) { - Theme::drawSlices(g, layer.spriteSheet(), rc, - layer.spriteBounds(), - layer.slicesBounds(), true); + !layer.spriteBounds().isEmpty()) { + if (!layer.slicesBounds().isEmpty()) { + Theme::drawSlices(g, layer.spriteSheet(), rc, + layer.spriteBounds(), + layer.slicesBounds(), true); - rc.x += layer.slicesBounds().x; - rc.y += layer.slicesBounds().y; - rc.w -= layer.spriteBounds().w - layer.slicesBounds().w; - rc.h -= layer.spriteBounds().h - layer.slicesBounds().h; + rc.x += layer.slicesBounds().x; + rc.y += layer.slicesBounds().y; + rc.w -= layer.spriteBounds().w - layer.slicesBounds().w; + rc.h -= layer.spriteBounds().h - layer.slicesBounds().h; + } + // Draw background as a solid piece + else { + g->drawRgbaSurface(layer.spriteSheet(), + layer.spriteBounds().x, + layer.spriteBounds().y, + rc.x, rc.y, + layer.spriteBounds().w, + layer.spriteBounds().h); + } } break; @@ -197,8 +310,9 @@ void Theme::paintLayer(Graphics* g, gfx::Size iconSize(icon->width(), icon->height()); gfx::Point pt; - if (layer.align() & LEFT) + if (layer.align() & LEFT) { pt.x = rc.x; + } else if (layer.align() & RIGHT) pt.x = rc.x+rc.w-iconSize.w; else @@ -242,12 +356,17 @@ void Theme::measureLayer(const Widget* widget, case Style::Layer::Type::kBackground: case Style::Layer::Type::kBorder: if (layer.spriteSheet() && - !layer.spriteBounds().isEmpty() && - !layer.slicesBounds().isEmpty()) { - borderHint.left(std::max(borderHint.left(), layer.slicesBounds().x)); - borderHint.top(std::max(borderHint.top(), layer.slicesBounds().y)); - borderHint.right(std::max(borderHint.right(), layer.spriteBounds().w - layer.slicesBounds().x2())); - borderHint.bottom(std::max(borderHint.bottom(), layer.spriteBounds().h - layer.slicesBounds().y2())); + !layer.spriteBounds().isEmpty()) { + if (!layer.slicesBounds().isEmpty()) { + borderHint.left(std::max(borderHint.left(), layer.slicesBounds().x)); + borderHint.top(std::max(borderHint.top(), layer.slicesBounds().y)); + borderHint.right(std::max(borderHint.right(), layer.spriteBounds().w - layer.slicesBounds().x2())); + borderHint.bottom(std::max(borderHint.bottom(), layer.spriteBounds().h - layer.slicesBounds().y2())); + } + else { + iconHint.w = std::max(iconHint.w, layer.spriteBounds().w); + iconHint.h = std::max(iconHint.h, layer.spriteBounds().h); + } } break; @@ -284,6 +403,53 @@ gfx::Border Theme::calcBorder(const Widget* widget, return borderHint; } +void Theme::calcSlices(const Widget* widget, + const Style* style, + gfx::Size& topLeft, + gfx::Size& center, + gfx::Size& bottomRight) +{ + ASSERT(widget); + ASSERT(style); + + for_each_layer( + widget, style, + [&topLeft, ¢er, &bottomRight] + (const Style::Layer& layer) { + if (layer.spriteSheet() && + !layer.spriteBounds().isEmpty() && + !layer.slicesBounds().isEmpty()) { + gfx::Rect sprite = layer.spriteBounds(); + gfx::Rect slices = layer.slicesBounds(); + topLeft.w = MAX(topLeft.w, slices.x); + topLeft.h = MAX(topLeft.h, slices.y); + center.w = MAX(center.w, slices.w); + center.h = MAX(center.h, slices.h); + bottomRight.w = MAX(bottomRight.w, sprite.w - slices.x2()); + bottomRight.h = MAX(bottomRight.h, sprite.h - slices.y2()); + } + }); +} + +gfx::Color Theme::calcBgColor(const Widget* widget, + const Style* style) +{ + ASSERT(widget); + ASSERT(style); + + gfx::Color bgColor = gfx::ColorNone; + + for_each_layer( + widget, style, + [&bgColor] + (const Style::Layer& layer) { + if (layer.type() == Style::Layer::Type::kBackground) + bgColor = layer.color(); + }); + + return bgColor; +} + void Theme::calcWidgetMetrics(const Widget* widget, const Style* style, gfx::Size& sizeHint, @@ -309,15 +475,17 @@ void Theme::calcWidgetMetrics(const Widget* widget, iconHint, iconAlign); }); - if (style->border().left() >= 0) borderHint.left(style->border().left()); - if (style->border().top() >= 0) borderHint.top(style->border().top()); - if (style->border().right() >= 0) borderHint.right(style->border().right()); - if (style->border().bottom() >= 0) borderHint.bottom(style->border().bottom()); + gfx::Border undef = Style::UndefinedBorder(); - if (style->padding().left() >= 0) paddingHint.left(style->padding().left()); - if (style->padding().top() >= 0) paddingHint.top(style->padding().top()); - if (style->padding().right() >= 0) paddingHint.right(style->padding().right()); - if (style->padding().bottom() >= 0) paddingHint.bottom(style->padding().bottom()); + if (style->border().left() != undef.left()) borderHint.left(style->border().left()); + if (style->border().top() != undef.top()) borderHint.top(style->border().top()); + if (style->border().right() != undef.right()) borderHint.right(style->border().right()); + if (style->border().bottom() != undef.bottom()) borderHint.bottom(style->border().bottom()); + + if (style->padding().left() != undef.left()) paddingHint.left(style->padding().left()); + if (style->padding().top() != undef.top()) paddingHint.top(style->padding().top()); + if (style->padding().right() != undef.right()) paddingHint.right(style->padding().right()); + if (style->padding().bottom() != undef.bottom()) paddingHint.bottom(style->padding().bottom()); sizeHint = gfx::Size(borderHint.width() + paddingHint.width(), borderHint.height() + paddingHint.height()); diff --git a/src/ui/theme.h b/src/ui/theme.h index 8d3d52fac..6d50d5704 100644 --- a/src/ui/theme.h +++ b/src/ui/theme.h @@ -49,7 +49,7 @@ namespace ui { virtual Cursor* getCursor(CursorType type) = 0; virtual void initWidget(Widget* widget) = 0; virtual void getWindowMask(Widget* widget, gfx::Region& region) = 0; - virtual void setDecorativeWidgetBounds(Widget* widget) = 0; + virtual void setDecorativeWidgetBounds(Widget* widget); virtual int getScrollbarSize() = 0; virtual gfx::Size getEntryCaretSize(Widget* widget) = 0; @@ -70,19 +70,32 @@ namespace ui { virtual void paintTextBox(PaintEvent& ev) = 0; virtual void paintViewScrollbar(PaintEvent& ev) = 0; virtual void paintViewViewport(PaintEvent& ev) = 0; - virtual void paintWindow(PaintEvent& ev) = 0; - virtual void paintPopupWindow(PaintEvent& ev) = 0; - virtual void paintTooltip(PaintEvent& ev) = 0; // Default implementation to draw widgets with new ui::Styles virtual void paintWidget(Graphics* g, const Widget* widget, const Style* style, - gfx::Rect rc); + const gfx::Rect& bounds); + + virtual void paintTooltip(Graphics* g, + const Widget* widget, + const Style* style, + const Style* arrowStyle, + const gfx::Rect& bounds, + const int arrowAlign, + const gfx::Rect& target); + virtual gfx::Size calcSizeHint(const Widget* widget, const Style* style); virtual gfx::Border calcBorder(const Widget* widget, const Style* style); + virtual void calcSlices(const Widget* widget, + const Style* style, + gfx::Size& topLeft, + gfx::Size& center, + gfx::Size& bottomRight); + virtual gfx::Color calcBgColor(const Widget* widget, + const Style* style); static void drawSlices(Graphics* g, she::Surface* sheet, diff --git a/src/ui/tooltips.cpp b/src/ui/tooltips.cpp index 84492491a..f6ca4dfad 100644 --- a/src/ui/tooltips.cpp +++ b/src/ui/tooltips.cpp @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -19,6 +19,7 @@ #include "ui/paint_event.h" #include "ui/size_hint_event.h" #include "ui/system.h" +#include "ui/textbox.h" #include "ui/theme.h" #include @@ -123,12 +124,20 @@ void TooltipManager::onTick() // TipWindow TipWindow::TipWindow(const std::string& text) - : PopupWindow(text, ClickBehavior::CloseOnClickInOtherWindow) + // Put an empty string in the ctor so the window label isn't build + : PopupWindow("", ClickBehavior::CloseOnClickInOtherWindow) + , m_arrowStyle(nullptr) , m_arrowAlign(0) , m_closeOnKeyDown(true) + , m_textBox(new TextBox("", LEFT | TOP)) { setTransparent(true); + // Here we build our own custimized label for the window + // (a text box). + addChild(m_textBox); + setText(text); + makeFixed(); initTheme(); } @@ -232,50 +241,17 @@ bool TipWindow::onProcessMessage(Message* msg) return PopupWindow::onProcessMessage(msg); } -void TipWindow::onSizeHint(SizeHintEvent& ev) -{ - ScreenGraphics g; - g.setFont(font()); - Size resultSize = - g.fitString(text(), - (clientBounds() - border()).w, - align()); - - resultSize.w += border().width(); - resultSize.h += border().height(); - - if (!children().empty()) { - Size maxSize(0, 0); - Size reqSize; - - for (auto child : children()) { - reqSize = child->sizeHint(); - - maxSize.w = MAX(maxSize.w, reqSize.w); - maxSize.h = MAX(maxSize.h, reqSize.h); - } - - resultSize.w = MAX(resultSize.w, maxSize.w + border().width()); - resultSize.h += maxSize.h; - } - - ev.setSizeHint(resultSize); -} - -void TipWindow::onInitTheme(InitThemeEvent& ev) -{ - Window::onInitTheme(ev); - - setBorder( - gfx::Border(6 * guiscale(), - 6 * guiscale(), - 6 * guiscale(), - 7 * guiscale())); -} - void TipWindow::onPaint(PaintEvent& ev) { - theme()->paintTooltip(ev); + theme()->paintTooltip( + ev.graphics(), this, style(), arrowStyle(), + clientBounds(), arrowAlign(), + gfx::Rect(target()).offset(-bounds().origin())); +} + +void TipWindow::onBuildTitleLabel() +{ + m_textBox->setText(text()); } } // namespace ui diff --git a/src/ui/tooltips.h b/src/ui/tooltips.h index db5cc9096..4168b6f48 100644 --- a/src/ui/tooltips.h +++ b/src/ui/tooltips.h @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2013, 2015 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -18,6 +18,7 @@ namespace ui { + class TextBox; class TipWindow; class TooltipManager : public Widget { @@ -58,6 +59,9 @@ namespace ui { public: TipWindow(const std::string& text = ""); + Style* arrowStyle() { return m_arrowStyle; } + void setArrowStyle(Style* style) { m_arrowStyle = style; } + int arrowAlign() const { return m_arrowAlign; } const gfx::Rect& target() const { return m_target; } @@ -67,16 +71,19 @@ namespace ui { // window. bool pointAt(int arrowAlign, const gfx::Rect& target); + TextBox* textBox() const { return m_textBox; } + protected: bool onProcessMessage(Message* msg) override; - void onSizeHint(SizeHintEvent& ev) override; - void onInitTheme(InitThemeEvent& ev) override; void onPaint(PaintEvent& ev) override; + void onBuildTitleLabel() override; private: + Style* m_arrowStyle; int m_arrowAlign; gfx::Rect m_target; bool m_closeOnKeyDown; + TextBox* m_textBox; }; } // namespace ui diff --git a/src/ui/ui.h b/src/ui/ui.h index c84f37aa2..fb01f89fa 100644 --- a/src/ui/ui.h +++ b/src/ui/ui.h @@ -25,6 +25,7 @@ #include "ui/hit_test_event.h" #include "ui/image_view.h" #include "ui/init_theme_event.h" +#include "ui/int_entry.h" #include "ui/keys.h" #include "ui/label.h" #include "ui/layout_io.h" diff --git a/src/ui/widget.cpp b/src/ui/widget.cpp index fe3961d4a..c79940538 100644 --- a/src/ui/widget.cpp +++ b/src/ui/widget.cpp @@ -176,6 +176,10 @@ void Widget::setBgColor(gfx::Color color) { m_bgColor = color; onSetBgColor(); + + if (m_style) { + LOG(WARNING) << "Warning setting bgColor to a widget with style\n"; + } } void Widget::setTheme(Theme* theme) @@ -188,6 +192,7 @@ void Widget::setStyle(Style* style) { m_style = style; m_border = m_theme->calcBorder(this, style); + m_bgColor = m_theme->calcBgColor(this, m_style); } // =============================================================== @@ -645,6 +650,7 @@ void Widget::setBoundsQuietly(const gfx::Rect& rc) void Widget::setBorder(const Border& br) { m_border = br; + if (m_style) { LOG(WARNING) << "Warning setting border to a widget with style\n"; } @@ -1484,9 +1490,8 @@ void Widget::onInitTheme(InitThemeEvent& ev) void Widget::onSetDecorativeWidgetBounds() { - if (m_theme) { + if (m_theme) m_theme->setDecorativeWidgetBounds(this); - } } void Widget::onVisible(bool visible) diff --git a/src/ui/widget_type.h b/src/ui/widget_type.h index 40bd98ebf..ad997d0a5 100644 --- a/src/ui/widget_type.h +++ b/src/ui/widget_type.h @@ -42,6 +42,8 @@ namespace ui { kViewViewportWidget, kViewWidget, kWindowWidget, + kWindowTitleLabelWidget, + kWindowCloseButtonWidget, // User widgets. kFirstUserWidget, diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 79f4a1f0f..74e9f4d09 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -11,14 +11,16 @@ #include "ui/window.h" #include "gfx/size.h" +#include "ui/button.h" #include "ui/graphics.h" #include "ui/intern.h" +#include "ui/label.h" #include "ui/manager.h" #include "ui/message.h" #include "ui/message_loop.h" #include "ui/move_region.h" -#include "ui/size_hint_event.h" #include "ui/resize_event.h" +#include "ui/size_hint_event.h" #include "ui/system.h" #include "ui/theme.h" @@ -26,6 +28,8 @@ namespace ui { using namespace gfx; +namespace { + enum { WINDOW_NONE = 0, WINDOW_MOVE = 1, @@ -35,25 +39,88 @@ enum { WINDOW_RESIZE_BOTTOM = 16, }; -static gfx::Point clickedMousePos; -static gfx::Rect* clickedWindowPos = NULL; +gfx::Point clickedMousePos; +gfx::Rect* clickedWindowPos = nullptr; + +class WindowTitleLabel : public Label { +public: + WindowTitleLabel(const std::string& text) : Label(text) { + setDecorative(true); + setType(kWindowTitleLabelWidget); + initTheme(); + } +}; + + +// Controls the "X" button in a window to close it. +class WindowCloseButton : public ButtonBase { +public: + WindowCloseButton() + : ButtonBase("", kWindowCloseButtonWidget, + kButtonWidget, kButtonWidget) { + setDecorative(true); + initTheme(); + } + +protected: + + void onClick(Event& ev) override { + ButtonBase::onClick(ev); + closeWindow(); + } + + bool onProcessMessage(Message* msg) override { + switch (msg->type()) { + + case kSetCursorMessage: + ui::set_mouse_cursor(kArrowCursor); + return true; + + case kKeyDownMessage: + if (window()->isForeground() && + static_cast(msg)->scancode() == kKeyEsc) { + setSelected(true); + return true; + } + break; + + case kKeyUpMessage: + if (window()->isForeground() && + static_cast(msg)->scancode() == kKeyEsc) { + if (isSelected()) { + setSelected(false); + closeWindow(); + return true; + } + } + break; + } + + return ButtonBase::onProcessMessage(msg); + } +}; + +} // anonymous namespace Window::Window(Type type, const std::string& text) : Widget(kWindowWidget) + , m_closer(nullptr) + , m_titleLabel(nullptr) + , m_closeButton(nullptr) + , m_isDesktop(type == DesktopWindow) + , m_isMoveable(!m_isDesktop) + , m_isSizeable(!m_isDesktop) + , m_isOnTop(false) + , m_isWantFocus(true) + , m_isForeground(false) + , m_isAutoRemap(true) { - m_closer = NULL; - m_isDesktop = (type == DesktopWindow); - m_isMoveable = !m_isDesktop; - m_isSizeable = !m_isDesktop; - m_isOnTop = false; - m_isWantFocus = true; - m_isForeground = false; - m_isAutoRemap = true; - setVisible(false); setAlign(LEFT | MIDDLE); - if (type == WithTitleBar) + if (type == WithTitleBar) { setText(text); + addChild(m_closeButton = new WindowCloseButton); + } initTheme(); } @@ -95,12 +162,6 @@ HitTest Window::hitTest(const gfx::Point& point) return ev.hit(); } -void Window::removeDecorativeWidgets() -{ - while (!children().empty()) - delete children().front(); -} - void Window::onClose(CloseEvent& ev) { // Fire Close signal @@ -112,11 +173,18 @@ void Window::onHitTest(HitTestEvent& ev) HitTest ht = HitTestNowhere; // If this window is not movable or we are not completely visible. - if (!m_isMoveable || - // TODO check why this is necessary, there should be a bug in - // the manager where we are receiving mouse events and are not - // the top most window. - this->manager()->pick(ev.point()) != this) { + if (!m_isMoveable) { + ev.setHit(ht); + return; + } + + // TODO check why this is necessary, there should be a bug in + // the manager where we are receiving mouse events and are not + // the top most window. + Widget* picked = manager()->pick(ev.point()); + if (picked && + picked != this && + picked->type() != kWindowTitleLabelWidget) { ev.setHit(ht); return; } @@ -444,19 +512,14 @@ void Window::onSizeHint(SizeHintEvent& ev) } } - if (hasText()) - maxSize.w = MAX(maxSize.w, textWidth()); + if (m_titleLabel) + maxSize.w = MAX(maxSize.w, m_titleLabel->sizeHint().w); ev.setSizeHint(maxSize.w + border().width(), maxSize.h + border().height()); } } -void Window::onPaint(PaintEvent& ev) -{ - theme()->paintWindow(ev); -} - void Window::onBroadcastMouseMessage(WidgetsList& targets) { targets.push_back(this); @@ -473,10 +536,30 @@ void Window::onBroadcastMouseMessage(WidgetsList& targets) void Window::onSetText() { + onBuildTitleLabel(); Widget::onSetText(); initTheme(); } +void Window::onBuildTitleLabel() +{ + if (text().empty()) { + if (m_titleLabel) { + removeChild(m_titleLabel); + delete m_titleLabel; + m_titleLabel = nullptr; + } + } + else { + if (!m_titleLabel) { + m_titleLabel = new WindowTitleLabel(text()); + addChild(m_titleLabel); + } + else + m_titleLabel->setText(text()); + } +} + void Window::windowSetPosition(const gfx::Rect& rect) { // Copy the new position rectangle diff --git a/src/ui/window.h b/src/ui/window.h index 68d6f5d42..069264b9c 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -17,6 +17,9 @@ namespace ui { + class ButtonBase; + class Label; + class Window : public Widget { public: enum Type { DesktopWindow, WithTitleBar, WithoutTitleBar }; @@ -51,8 +54,6 @@ namespace ui { HitTest hitTest(const gfx::Point& point); - void removeDecorativeWidgets(); - // Signals obs::signal Close; @@ -60,7 +61,6 @@ namespace ui { virtual bool onProcessMessage(Message* msg) override; virtual void onResize(ResizeEvent& ev) override; virtual void onSizeHint(SizeHintEvent& ev) override; - virtual void onPaint(PaintEvent& ev) override; virtual void onBroadcastMouseMessage(WidgetsList& targets) override; virtual void onSetText() override; @@ -69,6 +69,7 @@ namespace ui { virtual void onHitTest(HitTestEvent& ev); virtual void onWindowResize(); virtual void onWindowMovement(); + virtual void onBuildTitleLabel(); private: void windowSetPosition(const gfx::Rect& rect); @@ -77,6 +78,8 @@ namespace ui { void moveWindow(const gfx::Rect& rect, bool use_blit); Widget* m_closer; + Label* m_titleLabel; + ButtonBase* m_closeButton; bool m_isDesktop : 1; bool m_isMoveable : 1; bool m_isSizeable : 1;