Move all code to draw windows to ui-lib w/new styles

This commit is contained in:
David Capello 2017-02-17 14:18:47 -03:00
parent 2883a70e25
commit a62f12ad09
22 changed files with 544 additions and 472 deletions

View File

@ -412,24 +412,6 @@
<stylesheet>
<!-- window -->
<style id="window">
<background color="window_face" part="window" />
</style>
<style id="window_title">
<background color="window_titlebar_face" />
<text color="window_titlebar_text" align="left" valign="middle" />
</style>
<style id="menubox">
<background color="window_face" part="menu" />
</style>
<style id="desktop">
<background color="desktop" />
</style>
<!-- scrollbar -->
<style id="scrollbar">
<background part="scrollbar_bg" repeat="repeat" />
@ -835,6 +817,66 @@
<styles>
<style id="window_without_title" border="3">
<background color="window_face" />
<border part="menu" />
</style>
<style id="window_with_title" border="6" border-top="17">
<background color="window_face" />
<border part="window" />
</style>
<style id="window_title_label" margin-top="5" margin-left="5">
<background color="window_titlebar_face" />
<text color="window_titlebar_text" align="left middle" />
</style>
<style id="window_close_button" margin-top="3" margin-right="3">
<background part="window_button_normal" />
<background part="window_button_hot" state="mouse" />
<background part="window_button_selected" state="selected" />
<icon part="window_close_icon" color="button_normal_text" />
<icon part="window_close_icon" color="button_hot_text" state="mouse" />
<icon part="window_close_icon" color="button_selected_text" state="selected" />
</style>
<style id="popup_window">
<background color="window_face" />
<border part="menu" />
</style>
<style id="transparent_popup_window">
<!-- nothing (transparent) -->
</style>
<style id="menu">
<background color="window_face" />
</style>
<style id="menubox" extends="menu">
</style>
<style id="menubar" extends="menubox">
</style>
<style id="desktop">
<background color="desktop" />
</style>
<style id="tooltip_window">
<background part="tooltip" />
</style>
<style id="tooltip_window_arrow">
<background part="tooltip_arrow" />
</style>
<style id="tooltip_text">
<background color="tooltip_face" />
<text color="tooltip_text" />
</style>
<style id="label" padding="1">
<text color="text" align="left" />
<text color="disabled" align="left" state="disabled" />
@ -878,7 +920,7 @@
<border part="editor_selected" state="selected" />
</style>
<style id="workspace_view" border-top="4">
<style id="workspace_view" border-top="4" extends="view">
<border part="editor_normal" />
<border part="editor_selected" state="focus" />
</style>

View File

@ -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);
}
}

View File

@ -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"

View File

@ -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 {

View File

@ -241,8 +241,8 @@ bool PreviewEditorWindow::onProcessMessage(ui::Message* msg)
void PreviewEditorWindow::onClose(ui::CloseEvent& ev)
{
Button* closeButton = dynamic_cast<Button*>(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.

View File

@ -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<SkinTheme*>(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<KeyMessage*>(msg)->scancode() == kKeyEsc) {
setSelected(true);
return true;
}
break;
case kKeyUpMessage:
if (window()->isForeground() &&
static_cast<KeyMessage*>(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<Window*>(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<TipWindow*>(widget)) {
window->setStyle(newStyles.tooltipWindow());
window->setArrowStyle(newStyles.tooltipWindowArrow());
window->textBox()->setStyle(SkinTheme::instance()->newStyles.tooltipText());
}
else if (dynamic_cast<TransparentPopupWindow*>(widget)) {
widget->setStyle(newStyles.transparentPopupWindow());
}
else if (dynamic_cast<PopupWindow*>(widget)) {
widget->setStyle(newStyles.popupWindow());
}
else if (static_cast<Window*>(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<TipWindow*>(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<Window*>(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<Widget*>(ev.getSource());
Window* window = static_cast<Window*>(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<ButtonBase*>(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<ui::TipWindow*>(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();

View File

@ -39,7 +39,6 @@ namespace app {
, public app::gen::ThemeFile<SkinTheme> {
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

View File

@ -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<void, ToolBar, ToolBar>(&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);

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -85,14 +85,18 @@ namespace ui {
typedef std::vector<Layer> 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;
};

View File

@ -23,6 +23,7 @@
#include "ui/system.h"
#include "ui/view.h"
#include "ui/widget.h"
#include "ui/window.h"
#include <algorithm>
#include <cstring>
@ -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, &center, &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());

View File

@ -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,

View File

@ -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 <string>
@ -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

View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -42,6 +42,8 @@ namespace ui {
kViewViewportWidget,
kViewWidget,
kWindowWidget,
kWindowTitleLabelWidget,
kWindowCloseButtonWidget,
// User widgets.
kFirstUserWidget,

View File

@ -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<KeyMessage*>(msg)->scancode() == kKeyEsc) {
setSelected(true);
return true;
}
break;
case kKeyUpMessage:
if (window()->isForeground() &&
static_cast<KeyMessage*>(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

View File

@ -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<void (CloseEvent&)> 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;