diff --git a/data/extensions/aseprite-theme/theme.xml b/data/extensions/aseprite-theme/theme.xml index 817d6f10d..bf2d027c5 100644 --- a/data/extensions/aseprite-theme/theme.xml +++ b/data/extensions/aseprite-theme/theme.xml @@ -958,5 +958,11 @@ <icon part="aseprite_face_mouse" state="mouse" /> <icon part="aseprite_face_pushed" state="mouse selected" /> </style> + <style id="slider" border-top="4" border-bottom="5"> + <background part="slider_empty" /> + <background part="slider_empty_focused" state="focus" /> + <text color="slider_empty_text" align="center middle" /> + <text color="slider_empty_text" align="center middle" state="focus" y="1" /> + </style> </styles> </theme> diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp index efcccf066..0bf316fff 100644 --- a/src/app/ui/skin/skin_theme.cpp +++ b/src/app/ui/skin/skin_theme.cpp @@ -57,6 +57,39 @@ using namespace ui; // TODO For backward compatibility, in future versions we should remove this (extensions are preferred) const char* SkinTheme::kThemesFolderName = "themes"; +// This class offer backward compatibility with old themes, completing +// or changing styles from the default theme to match the default +// theme of previous versions, so third-party themes can look like +// they are running in the old Aseprite without any modification. +struct app::skin::SkinTheme::BackwardCompatibility { + bool hasSliderStyle = false; + void notifyStyleExistence(const char* styleId) { + if (std::strcmp(styleId, "slider") == 0) + hasSliderStyle = true; + } + void createMissingStyles(SkinTheme* theme) { + if (!hasSliderStyle && + theme->styles.slider()) { + // Old slider style + ui::Style style(nullptr); + os::Font* font = theme->getDefaultFont(); + const int h = font->height(); + + style.setId(theme->styles.slider()->id()); + style.setFont(font); + + auto part = theme->parts.sliderEmpty(); + style.setBorder( + gfx::Border(part->bitmapW()->width()-1*guiscale(), + part->bitmapN()->height()+h/2, + part->bitmapE()->width()-1*guiscale(), + part->bitmapS()->height()-1*guiscale()+h/2)); + + *theme->styles.slider() = style; + } + } +}; + static const char* g_cursor_names[kCursorTypes] = { "null", // kNoCursor "normal", // kArrowCursor @@ -224,7 +257,8 @@ void SkinTheme::onRegenerateTheme() // Then we load the selected theme to redefine default theme parts. if (pref.theme.selected.defaultValue() != pref.theme.selected()) { try { - loadAll(pref.theme.selected()); + BackwardCompatibility backward; + loadAll(pref.theme.selected(), &backward); } catch (const std::exception& e) { LOG("THEME: Error loading user-theme: %s\n", e.what()); @@ -265,7 +299,8 @@ void SkinTheme::loadFontData() } } -void SkinTheme::loadAll(const std::string& themeId) +void SkinTheme::loadAll(const std::string& themeId, + BackwardCompatibility* backward) { LOG("THEME: Loading theme %s\n", themeId.c_str()); @@ -277,7 +312,7 @@ void SkinTheme::loadAll(const std::string& themeId) throw base::Exception("Theme %s not found", themeId.c_str()); loadSheet(); - loadXml(); + loadXml(backward); } void SkinTheme::loadSheet() @@ -315,7 +350,7 @@ void SkinTheme::loadSheet() } } -void SkinTheme::loadXml() +void SkinTheme::loadXml(BackwardCompatibility* backward) { const int scale = guiscale(); @@ -515,6 +550,9 @@ void SkinTheme::loadXml() if (extends_id) base = m_styles[extends_id]; + if (backward) + backward->notifyStyleExistence(style_id); + ui::Style* style = m_styles[style_id]; if (!style) { m_styles[style_id] = style = new ui::Style(base); @@ -699,6 +737,9 @@ void SkinTheme::loadXml() } } + if (backward) + backward->createMissingStyles(this); + ThemeFile<SkinTheme>::updateInternals(); } @@ -851,13 +892,7 @@ void SkinTheme::initWidget(Widget* widget) break; case kSliderWidget: - BORDER4( - parts.sliderEmpty()->bitmapW()->width()-1*scale, - parts.sliderEmpty()->bitmapN()->height(), - parts.sliderEmpty()->bitmapE()->width()-1*scale, - parts.sliderEmpty()->bitmapS()->height()-1*scale); - widget->setChildSpacing(widget->textHeight()); - widget->setAlign(CENTER | MIDDLE); + widget->setStyle(styles.slider()); break; case kTextBoxWidget: @@ -1100,7 +1135,7 @@ void SkinTheme::drawEntryText(ui::Graphics* g, ui::Entry* widget) drawText( g, widget->getSuffix().c_str(), colors.entrySuffix(), ColorNone, - widget, sufBounds, 0, 0); + widget, sufBounds, widget->align(), 0); } } @@ -1187,8 +1222,8 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev) Rect pos = bounds; if (!bar) pos.offset(widget->childSpacing()/2, 0); - drawText(g, nullptr, fg, ColorNone, widget, pos, 0, - widget->mnemonic()); + drawText(g, nullptr, fg, ColorNone, widget, pos, + widget->align(), widget->mnemonic()); // For menu-box if (!bar) { @@ -1225,7 +1260,7 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev) std::string buf = appMenuItem->key()->accels().front().toString(); widget->setAlign(RIGHT | MIDDLE); - drawText(g, buf.c_str(), fg, ColorNone, widget, pos, 0, 0); + drawText(g, buf.c_str(), fg, ColorNone, widget, pos, widget->align(), 0); widget->setAlign(old_align); } } @@ -1236,7 +1271,7 @@ void SkinTheme::paintSlider(PaintEvent& ev) { Graphics* g = ev.graphics(); Slider* widget = static_cast<Slider*>(ev.getSource()); - Rect bounds = widget->clientBounds(); + const Rect bounds = widget->clientBounds(); int min, max, value; // Outside borders @@ -1246,14 +1281,15 @@ void SkinTheme::paintSlider(PaintEvent& ev) widget->getSliderThemeInfo(&min, &max, &value); - Rect rc(Rect(bounds).shrink(widget->border())); + Rect rc = bounds; + rc.shrink(widget->border()); int x; if (min != max) x = rc.x + rc.w * (value-min) / (max-min); else x = rc.x; - rc = widget->clientBounds(); + rc = bounds; // The mini-look is used for sliders with tiny borders. bool isMiniLook = false; @@ -1328,12 +1364,16 @@ void SkinTheme::paintSlider(PaintEvent& ev) std::string old_text = widget->text(); widget->setTextQuiet(widget->convertValueToText(value)); + gfx::Rect textrc; + int textAlign; + calcTextInfo(widget, widget->style(), bounds, textrc, textAlign); + { - IntersectClip clip(g, Rect(rc.x, rc.y, x-rc.x, rc.h)); + IntersectClip clip(g, Rect(rc.x, rc.y, x-rc.x+1, rc.h)); if (clip) { drawText(g, nullptr, colors.sliderFullText(), ColorNone, - widget, rc, 0, widget->mnemonic()); + widget, textrc, textAlign, widget->mnemonic()); } } @@ -1342,7 +1382,7 @@ void SkinTheme::paintSlider(PaintEvent& ev) if (clip) { drawText(g, nullptr, colors.sliderEmptyText(), - ColorNone, widget, rc, 0, widget->mnemonic()); + ColorNone, widget, textrc, textAlign, widget->mnemonic()); } } @@ -1400,9 +1440,13 @@ gfx::Color SkinTheme::getWidgetBgColor(Widget* widget) return colors.face(); } -void SkinTheme::drawText(Graphics* g, const char *t, gfx::Color fg_color, gfx::Color bg_color, - Widget* widget, const Rect& rc, - int selected_offset, int mnemonic) +void SkinTheme::drawText(Graphics* g, const char* t, + const gfx::Color fgColor, + const gfx::Color bgColor, + const Widget* widget, + const Rect& rc, + const int textAlign, + const int mnemonic) { if (t || widget->hasText()) { Rect textrc; @@ -1416,33 +1460,28 @@ void SkinTheme::drawText(Graphics* g, const char *t, gfx::Color fg_color, gfx::C // Horizontally text alignment - if (widget->align() & RIGHT) + if (textAlign & RIGHT) textrc.x = rc.x + rc.w - textrc.w - 1; - else if (widget->align() & CENTER) + else if (textAlign & CENTER) textrc.x = rc.center().x - textrc.w/2; else textrc.x = rc.x; // Vertically text alignment - if (widget->align() & BOTTOM) + if (textAlign & BOTTOM) textrc.y = rc.y + rc.h - textrc.h - 1; - else if (widget->align() & MIDDLE) + else if (textAlign & MIDDLE) textrc.y = rc.center().y - textrc.h/2; else textrc.y = rc.y; - if (widget->isSelected()) { - textrc.x += selected_offset; - textrc.y += selected_offset; - } - // Background - if (!is_transparent(bg_color)) { + if (!is_transparent(bgColor)) { if (!widget->isEnabled()) - g->fillRect(bg_color, Rect(textrc).inflate(guiscale(), guiscale())); + g->fillRect(bgColor, Rect(textrc).inflate(guiscale(), guiscale())); else - g->fillRect(bg_color, textrc); + g->fillRect(bgColor, textrc); } // Text @@ -1467,9 +1506,9 @@ void SkinTheme::drawText(Graphics* g, const char *t, gfx::Color fg_color, gfx::C t, (!widget->isEnabled() ? colors.disabled(): - (gfx::geta(fg_color) > 0 ? fg_color : - colors.text())), - bg_color, textrc.origin(), + (gfx::geta(fgColor) > 0 ? fgColor : + colors.text())), + bgColor, textrc.origin(), mnemonic); } } diff --git a/src/app/ui/skin/skin_theme.h b/src/app/ui/skin/skin_theme.h index 3cf4f4ba5..83ef78f5d 100644 --- a/src/app/ui/skin/skin_theme.h +++ b/src/app/ui/skin/skin_theme.h @@ -130,16 +130,24 @@ namespace app { void onRegenerateTheme() override; private: + struct BackwardCompatibility; + void loadFontData(); - void loadAll(const std::string& themeId); + void loadAll(const std::string& themeId, + BackwardCompatibility* backward = nullptr); void loadSheet(); - void loadXml(); + void loadXml(BackwardCompatibility* backward); os::Surface* sliceSheet(os::Surface* sur, const gfx::Rect& bounds); gfx::Color getWidgetBgColor(ui::Widget* widget); - void drawText(ui::Graphics* g, const char *t, gfx::Color fg_color, gfx::Color bg_color, - ui::Widget* widget, const gfx::Rect& rc, - int selected_offset, int mnemonic); + void drawText(ui::Graphics* g, + const char* t, + const gfx::Color fgColor, + const gfx::Color bgColor, + const ui::Widget* widget, + const gfx::Rect& rc, + const int textAlign, + const int mnemonic); void drawEntryText(ui::Graphics* g, ui::Entry* widget); std::string findThemePath(const std::string& themeId) const; diff --git a/src/ui/slider.cpp b/src/ui/slider.cpp index 967d376bc..01897a0f6 100644 --- a/src/ui/slider.cpp +++ b/src/ui/slider.cpp @@ -215,20 +215,6 @@ not_used:; return Widget::onProcessMessage(msg); } -void Slider::onSizeHint(SizeHintEvent& ev) -{ - int min_w = font()->textLength(convertValueToText(m_min)); - int max_w = font()->textLength(convertValueToText(m_max)); - - int w = std::max(min_w, max_w); - int h = textHeight(); - - w += border().width(); - h += border().height(); - - ev.setSizeHint(w, h); -} - void Slider::onPaint(PaintEvent& ev) { theme()->paintSlider(ev); diff --git a/src/ui/slider.h b/src/ui/slider.h index e3222c0a7..35c1a5b31 100644 --- a/src/ui/slider.h +++ b/src/ui/slider.h @@ -1,4 +1,5 @@ // Aseprite UI Library +// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2001-2016 David Capello // // This file is released under the terms of the MIT license. @@ -46,7 +47,6 @@ namespace ui { protected: // Events bool onProcessMessage(Message* msg) override; - void onSizeHint(SizeHintEvent& ev) override; void onPaint(PaintEvent& ev) override; // New events diff --git a/src/ui/theme.cpp b/src/ui/theme.cpp index c4ec947f3..c5a1c4754 100644 --- a/src/ui/theme.cpp +++ b/src/ui/theme.cpp @@ -490,15 +490,34 @@ gfx::Size Theme::calcSizeHint(const Widget* widget, { gfx::Size sizeHint; gfx::Border borderHint; - calcWidgetMetrics(widget, style, sizeHint, borderHint); + gfx::Rect textHint; + int textAlign; + calcWidgetMetrics(widget, style, sizeHint, borderHint, + textHint, textAlign); return sizeHint; } +void Theme::calcTextInfo(const Widget* widget, + const Style* style, + const gfx::Rect& bounds, + gfx::Rect& textBounds, int& textAlign) +{ + gfx::Size sizeHint; + gfx::Border borderHint; + gfx::Rect textHint; + calcWidgetMetrics(widget, style, sizeHint, borderHint, + textHint, textAlign); + + textBounds = bounds; + textBounds.shrink(borderHint); + textBounds.offset(textHint.origin()); +} + void Theme::measureLayer(const Widget* widget, const Style* style, const Style::Layer& layer, gfx::Border& borderHint, - gfx::Size& textHint, int& textAlign, + gfx::Rect& textHint, int& textAlign, gfx::Size& iconHint, int& iconAlign) { ASSERT(style); @@ -531,6 +550,7 @@ void Theme::measureLayer(const Widget* widget, gfx::Size textSize(Graphics::measureUITextLength(widget->text(), font), font->height()); + textHint.offset(layer.offset()); textHint.w = std::max(textHint.w, textSize.w+ABS(layer.offset().x)); textHint.h = std::max(textHint.h, textSize.h+ABS(layer.offset().y)); textAlign = layer.align(); @@ -555,7 +575,10 @@ gfx::Border Theme::calcBorder(const Widget* widget, { gfx::Size sizeHint; gfx::Border borderHint; - calcWidgetMetrics(widget, style, sizeHint, borderHint); + gfx::Rect textHint; + int textAlign; + calcWidgetMetrics(widget, style, sizeHint, borderHint, + textHint, textAlign); return borderHint; } @@ -610,7 +633,8 @@ gfx::Color Theme::calcBgColor(const Widget* widget, void Theme::calcWidgetMetrics(const Widget* widget, const Style* style, gfx::Size& sizeHint, - gfx::Border& borderHint) + gfx::Border& borderHint, + gfx::Rect& textHint, int& textAlign) { ASSERT(widget); ASSERT(style); @@ -619,9 +643,11 @@ void Theme::calcWidgetMetrics(const Widget* widget, borderHint = gfx::Border(0, 0, 0, 0); gfx::Border paddingHint(0, 0, 0, 0); - gfx::Size textHint(0, 0); + + textHint = gfx::Rect(0, 0, 0, 0); + textAlign = CENTER | MIDDLE; + gfx::Size iconHint(0, 0); - int textAlign = CENTER | MIDDLE; int iconAlign = CENTER | MIDDLE; for_each_layer( diff --git a/src/ui/theme.h b/src/ui/theme.h index 683e08a72..05d0341d2 100644 --- a/src/ui/theme.h +++ b/src/ui/theme.h @@ -1,4 +1,5 @@ // Aseprite UI Library +// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. @@ -109,6 +110,10 @@ namespace ui { gfx::Size& topLeft, gfx::Size& center, gfx::Size& bottomRight); + virtual void calcTextInfo(const Widget* widget, + const Style* style, + const gfx::Rect& bounds, + gfx::Rect& textBounds, int& textAlign); virtual gfx::Color calcBgColor(const Widget* widget, const Style* style); @@ -139,12 +144,13 @@ namespace ui { const Style* style, const Style::Layer& layer, gfx::Border& borderHint, - gfx::Size& textHint, int& textAlign, + gfx::Rect& textHint, int& textAlign, gfx::Size& iconHint, int& iconAlign); void calcWidgetMetrics(const Widget* widget, const Style* style, gfx::Size& sizeHint, - gfx::Border& borderHint); + gfx::Border& borderHint, + gfx::Rect& textHint, int& textAlign); }; } // namespace ui