aseprite/src/ui/theme.cpp

987 lines
28 KiB
C++
Raw Normal View History

// Aseprite UI Library
2024-02-21 02:40:17 +08:00
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
2007-09-19 07:57:02 +08:00
#ifdef HAVE_CONFIG_H
2009-07-13 04:29:16 +08:00
#include "config.h"
#endif
2009-07-13 04:29:16 +08:00
#include "ui/theme.h"
#include "gfx/point.h"
#include "gfx/size.h"
2018-08-09 23:58:43 +08:00
#include "os/surface.h"
#include "os/system.h"
2024-02-21 02:40:17 +08:00
#include "text/font.h"
2012-06-18 09:49:58 +08:00
#include "ui/intern.h"
#include "ui/manager.h"
#include "ui/paint_event.h"
#include "ui/scale.h"
#include "ui/size_hint_event.h"
#include "ui/style.h"
2012-06-18 09:49:58 +08:00
#include "ui/system.h"
#include "ui/view.h"
#include "ui/widget.h"
#include "ui/window.h"
2007-09-19 07:57:02 +08:00
#include <algorithm>
#include <cstring>
namespace ui {
namespace {
int current_ui_scale = 1; // Global UI Screen Scaling factor
int old_ui_scale = 1; // Add this field in InitThemeEvent
Theme* current_theme = nullptr; // Global active theme
int compare_layer_flags(int a, int b)
{
// TODO improve this
return a - b;
}
void for_each_layer(const int flags,
const Style* style,
std::function<void(const Style::Layer&)> callback)
{
ASSERT(style);
if (!style)
return;
const Style::Layer* bestLayer = nullptr;
for (const auto& layer : style->layers()) {
if (bestLayer &&
bestLayer->type() != layer.type()) {
callback(*bestLayer);
bestLayer = nullptr;
}
if ((!layer.flags()
|| (layer.flags() & flags) == layer.flags())
&& (!bestLayer
|| (bestLayer && compare_layer_flags(bestLayer->flags(), layer.flags()) <= 0))) {
bestLayer = &layer;
}
}
if (bestLayer)
callback(*bestLayer);
}
void for_each_layer(const Widget* widget,
const Style* style,
std::function<void(const Style::Layer&)> callback)
{
for_each_layer(
PaintWidgetPartInfo::getStyleFlagsForWidget(widget),
style,
callback);
}
std::function<void(int srcx, int srcy, int dstx, int dsty, int w, int h)>
2018-08-09 23:58:43 +08:00
getDrawSurfaceFunction(Graphics* g, os::Surface* sheet, gfx::Color color)
{
if (color != gfx::ColorNone)
return [g, sheet, color](int srcx, int srcy, int dstx, int dsty, int w, int h) {
g->drawColoredRgbaSurface(sheet, color, srcx, srcy, dstx, dsty, w, h);
};
else
return [g, sheet](int srcx, int srcy, int dstx, int dsty, int w, int h) {
g->drawRgbaSurface(sheet, srcx, srcy, dstx, dsty, w, h);
};
}
} // anonymous namespace
2007-09-19 07:57:02 +08:00
PaintWidgetPartInfo::PaintWidgetPartInfo()
{
bgColor = gfx::ColorNone;
styleFlags = 0;
text = nullptr;
mnemonic = 0;
icon = nullptr;
}
PaintWidgetPartInfo::PaintWidgetPartInfo(const Widget* widget)
{
bgColor = (!widget->isTransparent() ?
widget->bgColor():
gfx::ColorNone);
styleFlags = PaintWidgetPartInfo::getStyleFlagsForWidget(widget);
text = &widget->text();
mnemonic = widget->mnemonic();
icon = nullptr;
if (const Style::Layer::IconSurfaceProvider* iconProvider = dynamic_cast<const Style::Layer::IconSurfaceProvider*>(widget)) {
icon = iconProvider->iconSurface();
}
}
// static
int PaintWidgetPartInfo::getStyleFlagsForWidget(const Widget* widget)
{
return
(widget->isEnabled() ? 0: Style::Layer::kDisabled) |
(widget->isSelected() ? Style::Layer::kSelected: 0) |
(widget->hasMouse() ? Style::Layer::kMouse: 0) |
(widget->hasFocus() ? Style::Layer::kFocus: 0) |
(widget->hasCapture() ? Style::Layer::kCapture: 0);
}
Theme::Theme()
2007-09-19 07:57:02 +08:00
{
}
Theme::~Theme()
{
if (current_theme == this)
set_theme(nullptr, guiscale());
}
// static
ui::Style Theme::m_defaultStyle(nullptr);
void Theme::regenerateTheme()
2007-09-19 07:57:02 +08:00
{
set_mouse_cursor(kNoCursor);
onRegenerateTheme();
2007-09-19 07:57:02 +08:00
}
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::paintWidgetPart(Graphics* g,
const Style* style,
const gfx::Rect& bounds,
const PaintWidgetPartInfo& info)
{
ASSERT(g);
ASSERT(style);
// External background
if (!gfx::is_transparent(info.bgColor))
g->fillRect(info.bgColor, bounds);
gfx::Rect rc = bounds;
gfx::Color outBgColor = gfx::ColorNone;
for_each_layer(
info.styleFlags, style,
[this, g, style, &info, &rc, &outBgColor]
(const Style::Layer& layer) {
paintLayer(g, style, layer,
(info.text ? *info.text: std::string()),
info.mnemonic, info.icon, rc, outBgColor);
});
}
void Theme::paintWidget(Graphics* g,
const Widget* widget,
const Style* style,
const gfx::Rect& bounds)
{
ASSERT(widget);
PaintWidgetPartInfo info(widget);
paintWidgetPart(g, style, bounds, info);
}
2017-03-11 09:42:15 +08:00
void Theme::paintScrollBar(Graphics* g,
const Widget* widget,
const Style* style,
const Style* thumbStyle,
const gfx::Rect& bounds,
const gfx::Rect& thumbBounds)
{
PaintWidgetPartInfo info(widget);
paintWidgetPart(g, style, bounds, info);
// TODO flags for the thumb could have "mouse" only
// when the mouse is inside the thumb
info.bgColor = gfx::ColorNone;
paintWidgetPart(g, thumbStyle, thumbBounds, info);
2017-03-11 09:42:15 +08:00
}
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 && arrowAlign) {
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::paintTextBoxWithStyle(Graphics* g,
const Widget* widget)
{
gfx::Color bg = gfx::ColorNone, fg = gfx::ColorNone;
for_each_layer(
PaintWidgetPartInfo::getStyleFlagsForWidget(widget),
widget->style(),
[&fg, &bg](const Style::Layer& layer) {
switch (layer.type()) {
case Style::Layer::Type::kBackground: bg = layer.color(); break;
case Style::Layer::Type::kText: fg = layer.color(); break;
}
});
if (fg != gfx::ColorNone)
Theme::drawTextBox(g, widget, nullptr, nullptr, bg, fg);
}
void Theme::paintLayer(Graphics* g,
const Style* style,
const Style::Layer& layer,
const std::string& text,
const int mnemonic,
os::Surface* providedIcon,
gfx::Rect& rc,
gfx::Color& bgColor)
{
ASSERT(style);
if (!style)
return;
switch (layer.type()) {
case Style::Layer::Type::kBackground:
case Style::Layer::Type::kBackgroundBorder:
if (layer.spriteSheet() &&
!layer.spriteBounds().isEmpty()) {
if (!layer.slicesBounds().isEmpty()) {
Theme::drawSlices(g, layer.spriteSheet(), rc,
layer.spriteBounds(),
layer.slicesBounds(),
layer.color(), true);
if (layer.type() == Style::Layer::Type::kBackgroundBorder) {
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 using different methods
else {
IntersectClip clip(g, rc);
if (clip) {
auto draw = getDrawSurfaceFunction(
g, layer.spriteSheet(), layer.color());
switch (layer.align()) {
// Horizontal line
case MIDDLE:
for (int x=rc.x; x<rc.x2(); x+=layer.spriteBounds().w) {
draw(layer.spriteBounds().x,
layer.spriteBounds().y,
x, rc.y+rc.h/2-layer.spriteBounds().h/2,
layer.spriteBounds().w,
layer.spriteBounds().h);
}
break;
// Vertical line
case CENTER:
for (int y=rc.y; y<rc.y2(); y+=layer.spriteBounds().h) {
draw(layer.spriteBounds().x,
layer.spriteBounds().y,
rc.x+rc.w/2-layer.spriteBounds().w/2, y,
layer.spriteBounds().w,
layer.spriteBounds().h);
}
break;
// One instance
case CENTER | MIDDLE:
draw(layer.spriteBounds().x,
layer.spriteBounds().y,
rc.x+rc.w/2-layer.spriteBounds().w/2,
rc.y+rc.h/2-layer.spriteBounds().h/2,
layer.spriteBounds().w,
layer.spriteBounds().h);
break;
// Pattern
case 0:
for (int y=rc.y; y<rc.y2(); y+=layer.spriteBounds().h) {
for (int x=rc.x; x<rc.x2(); x+=layer.spriteBounds().w)
draw(layer.spriteBounds().x,
layer.spriteBounds().y,
x, y,
layer.spriteBounds().w,
layer.spriteBounds().h);
}
break;
}
}
}
}
else if (layer.color() != gfx::ColorNone) {
bgColor = layer.color();
g->fillRect(layer.color(), rc);
}
break;
case Style::Layer::Type::kBorder:
if (layer.spriteSheet() &&
!layer.spriteBounds().isEmpty() &&
!layer.slicesBounds().isEmpty()) {
Theme::drawSlices(g, layer.spriteSheet(), rc,
layer.spriteBounds(),
layer.slicesBounds(),
layer.color(), false);
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;
}
else if (layer.color() != gfx::ColorNone) {
g->drawRect(layer.color(), rc);
}
break;
case Style::Layer::Type::kText:
2024-08-19 23:03:31 +08:00
if (text.empty())
break;
if (layer.color() != gfx::ColorNone) {
2024-02-21 02:40:17 +08:00
text::FontRef oldFont = base::AddRef(g->font());
if (style->font())
2020-07-08 06:06:48 +08:00
g->setFont(AddRef(style->font()));
2017-03-11 06:23:43 +08:00
if (layer.align() & WORDWRAP) {
gfx::Rect textBounds = rc;
textBounds.offset(layer.offset());
g->drawAlignedUIText(text,
2017-03-11 06:23:43 +08:00
layer.color(),
bgColor,
textBounds, layer.align());
}
else {
gfx::Size textSize = g->measureUIText(text);
2017-03-11 06:23:43 +08:00
gfx::Point pt;
gfx::Border undef = Style::UndefinedBorder();
gfx::Border padding = style->padding();
if (padding.left() == undef.left()) padding.left(0);
if (padding.right() == undef.right()) padding.right(0);
if (padding.top() == undef.top()) padding.top(0);
if (padding.bottom() == undef.bottom()) padding.bottom(0);
2017-03-11 06:23:43 +08:00
if (layer.align() & LEFT)
pt.x = rc.x+padding.left();
2017-03-11 06:23:43 +08:00
else if (layer.align() & RIGHT)
pt.x = rc.x+rc.w-textSize.w-padding.right();
else {
pt.x = CALC_FOR_CENTER(rc.x+padding.left(), rc.w-padding.width(), textSize.w);
}
2017-03-11 06:23:43 +08:00
if (layer.align() & TOP)
pt.y = rc.y+padding.top();
2017-03-11 06:23:43 +08:00
else if (layer.align() & BOTTOM)
pt.y = rc.y+rc.h-textSize.h-padding.bottom();
else {
pt.y = CALC_FOR_CENTER(rc.y+padding.top(), rc.h-padding.height(), textSize.h);
}
2017-03-11 06:23:43 +08:00
pt += layer.offset();
g->drawUIText(text,
2017-03-11 06:23:43 +08:00
layer.color(),
bgColor,
pt, style->mnemonics() ? mnemonic : 0);
2017-03-11 06:23:43 +08:00
}
if (style->font())
g->setFont(oldFont);
}
break;
case Style::Layer::Type::kIcon: {
os::Surface* icon = providedIcon ? providedIcon : layer.icon();
if (icon) {
gfx::Size iconSize(icon->width(), icon->height());
gfx::Point pt;
gfx::Border undef = Style::UndefinedBorder();
gfx::Border padding = style->padding();
if (padding.left() == undef.left()) padding.left(0);
if (padding.right() == undef.right()) padding.right(0);
if (padding.top() == undef.top()) padding.top(0);
if (padding.bottom() == undef.bottom()) padding.bottom(0);
if (layer.align() & LEFT)
pt.x = rc.x+padding.left();
else if (layer.align() & RIGHT)
pt.x = rc.x+rc.w-iconSize.w-padding.right();
else {
pt.x = CALC_FOR_CENTER(rc.x+padding.left(), rc.w-padding.width(), iconSize.w);
}
if (layer.align() & TOP)
pt.y = rc.y+padding.top();
else if (layer.align() & BOTTOM)
pt.y = rc.y+rc.h-iconSize.h-padding.bottom();
else {
pt.y = CALC_FOR_CENTER(rc.y+padding.top(), rc.h-padding.height(), iconSize.h);
}
pt += layer.offset();
if (layer.color() != gfx::ColorNone)
g->drawColoredRgbaSurface(icon, layer.color(), pt.x, pt.y);
else
g->drawRgbaSurface(icon, pt.x, pt.y);
}
break;
}
}
}
gfx::Size Theme::calcSizeHint(const Widget* widget,
const Style* style)
{
gfx::Size sizeHint;
gfx::Border 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::Rect& textHint, int& textAlign,
gfx::Size& iconHint, int& iconAlign)
{
ASSERT(style);
if (!style)
return;
switch (layer.type()) {
case Style::Layer::Type::kBackground:
case Style::Layer::Type::kBackgroundBorder:
case Style::Layer::Type::kBorder:
if (layer.spriteSheet() &&
!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;
case Style::Layer::Type::kText:
if (layer.color() != gfx::ColorNone) {
gfx::Size textSize;
if (style->font() &&
style->font() != widget->font()) {
text::Font* font = style->font();
textSize = gfx::Size(Graphics::measureUITextLength(widget->text(), font),
font->height());
}
else {
// We can use Widget::textSize() because we're going to use
// the widget font and, probably, the cached TextBlob.
textSize = widget->textSize();
}
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();
}
break;
case Style::Layer::Type::kIcon: {
const os::Surface* icon = layer.icon();
if (const Style::Layer::IconSurfaceProvider* iconProvider = dynamic_cast<const Style::Layer::IconSurfaceProvider*>(widget)) {
icon = iconProvider->iconSurface() ? iconProvider->iconSurface() : icon;
}
if (icon) {
iconHint.w = std::max(iconHint.w, icon->width()+ABS(layer.offset().x));
iconHint.h = std::max(iconHint.h, icon->height()+ABS(layer.offset().y));
iconAlign = layer.align();
}
break;
}
}
}
gfx::Border Theme::calcBorder(const Widget* widget,
const Style* style)
{
gfx::Size sizeHint;
gfx::Border borderHint;
gfx::Rect textHint;
int textAlign;
calcWidgetMetrics(widget, style, sizeHint, borderHint,
textHint, textAlign);
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 = std::max(topLeft.w, slices.x);
topLeft.h = std::max(topLeft.h, slices.y);
center.w = std::max(center.w, slices.w);
center.h = std::max(center.h, slices.h);
bottomRight.w = std::max(bottomRight.w, sprite.w - slices.x2());
bottomRight.h = std::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 ||
layer.type() == Style::Layer::Type::kBackgroundBorder)
bgColor = layer.color();
});
return bgColor;
}
gfx::Size Theme::calcMinSize(const Widget* widget,
const Style* style)
{
ASSERT(widget);
ASSERT(style);
gfx::Size sz = widget->minSize();
if (sz.w == 0 || style->minSize().w > 0)
sz.w = style->minSize().w;
if (sz.h == 0 || style->minSize().h > 0)
sz.h = style->minSize().h;
return sz;
}
gfx::Size Theme::calcMaxSize(const Widget* widget,
const Style* style)
{
ASSERT(widget);
ASSERT(style);
gfx::Size sz = widget->maxSize();
int maxInt = std::numeric_limits<int>::max();
if (sz.w == maxInt || style->maxSize().w < maxInt)
sz.w = style->maxSize().w;
if (sz.h == maxInt || style->maxSize().h < maxInt)
sz.h = style->maxSize().h;
return sz;
}
void Theme::calcWidgetMetrics(const Widget* widget,
const Style* style,
gfx::Size& sizeHint,
gfx::Border& borderHint,
gfx::Rect& textHint, int& textAlign)
{
ASSERT(widget);
ASSERT(style);
if (!style)
return;
borderHint = gfx::Border(0, 0, 0, 0);
gfx::Border paddingHint(0, 0, 0, 0);
textHint = gfx::Rect(0, 0, 0, 0);
textAlign = CENTER | MIDDLE;
gfx::Size iconHint(0, 0);
int iconAlign = CENTER | MIDDLE;
for_each_layer(
widget, style,
[this, widget, style, &borderHint,
&textHint, &textAlign, &iconHint, &iconAlign]
(const Style::Layer& layer) {
measureLayer(widget, style, layer,
borderHint,
textHint, textAlign,
iconHint, iconAlign);
});
gfx::Border undef = Style::UndefinedBorder();
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());
if ((textAlign & (LEFT | CENTER | RIGHT)) == (iconAlign & (LEFT | CENTER | RIGHT)))
sizeHint.w += std::max(textHint.w, iconHint.w);
else
sizeHint.w += textHint.w + iconHint.w;
if ((textAlign & (TOP | MIDDLE | BOTTOM)) == (iconAlign & (TOP | MIDDLE | BOTTOM)))
sizeHint.h += std::max(textHint.h, iconHint.h);
else
sizeHint.h += textHint.h + iconHint.h;
sizeHint.w = std::clamp(sizeHint.w, widget->minSize().w, widget->maxSize().w);
sizeHint.h = std::clamp(sizeHint.h, widget->minSize().h, widget->maxSize().h);
}
//////////////////////////////////////////////////////////////////////
2007-09-19 07:57:02 +08:00
void set_theme(Theme* theme, const int uiscale)
2007-09-19 07:57:02 +08:00
{
old_ui_scale = current_ui_scale;
current_ui_scale = uiscale;
if (theme) {
theme->regenerateTheme();
current_theme = theme;
2007-09-19 07:57:02 +08:00
// Set the theme for all widgets
details::reinitThemeForAllWidgets();
// Reinitialize all widget using the new theme/uiscale
if (Manager* manager = Manager::getDefault()) {
manager->initTheme();
manager->invalidate();
}
2007-09-19 07:57:02 +08:00
}
old_ui_scale = current_ui_scale;
2007-09-19 07:57:02 +08:00
}
Theme* get_theme()
2007-09-19 07:57:02 +08:00
{
return current_theme;
2007-09-19 07:57:02 +08:00
}
int guiscale()
{
return current_ui_scale;
}
int details::old_guiscale()
{
return old_ui_scale;
}
// static
2018-08-09 23:58:43 +08:00
void Theme::drawSlices(Graphics* g, os::Surface* sheet,
const gfx::Rect& rc,
const gfx::Rect& sprite,
const gfx::Rect& slices,
const gfx::Color color,
const bool drawCenter)
{
Paint paint;
paint.color(color);
g->drawSurfaceNine(sheet, sprite, slices, rc, drawCenter, &paint);
}
// static
void Theme::drawTextBox(Graphics* g, const Widget* widget,
int* w, int* h, gfx::Color bg, gfx::Color fg)
2007-09-19 07:57:02 +08:00
{
View* view = (g ? View::getView(widget): nullptr);
char* text = const_cast<char*>(widget->text().c_str());
char* beg, *end;
2016-12-07 08:29:14 +08:00
int x1, y1;
2007-09-19 07:57:02 +08:00
int x, y, chr, len;
gfx::Point scroll;
int textheight = widget->textHeight();
2024-02-21 02:40:17 +08:00
text::Font* font = widget->font();
2007-09-19 07:57:02 +08:00
char *beg_end, *old_end;
int width;
2016-11-22 23:47:46 +08:00
gfx::Rect vp;
2007-09-19 07:57:02 +08:00
if (view) {
2016-11-22 23:47:46 +08:00
vp = view->viewportBounds().offset(-widget->bounds().origin());
scroll = view->viewScroll();
2007-09-19 07:57:02 +08:00
}
else {
2016-11-22 23:47:46 +08:00
vp = widget->clientBounds();
scroll.x = scroll.y = 0;
2007-09-19 07:57:02 +08:00
}
2016-12-07 08:29:14 +08:00
x1 = widget->clientBounds().x + widget->border().left();
y1 = widget->clientBounds().y + widget->border().top();
2007-09-19 07:57:02 +08:00
2016-11-23 00:33:01 +08:00
// Fill background
if (g)
g->fillRect(bg, vp);
2007-09-19 07:57:02 +08:00
chr = 0;
// Without word-wrap
if (!(widget->align() & WORDWRAP)) {
2016-12-07 08:29:14 +08:00
width = widget->clientChildrenBounds().w;
2007-09-19 07:57:02 +08:00
}
// With word-wrap
2007-09-19 07:57:02 +08:00
else {
if (w) {
width = *w;
*w = 0;
}
else {
2016-11-22 23:47:46 +08:00
// TODO modificable option? I don't think so, this is very internal stuff
2007-09-19 07:57:02 +08:00
#if 0
2016-11-22 23:47:46 +08:00
// Shows more information in x-scroll 0
width = vp.w;
2007-09-19 07:57:02 +08:00
#else
2016-11-22 23:47:46 +08:00
// Make good use of the complete text-box
2007-09-19 07:57:02 +08:00
if (view) {
gfx::Size maxSize = view->getScrollableSize();
width = std::max(vp.w, maxSize.w);
2007-09-19 07:57:02 +08:00
}
else {
2016-11-22 23:47:46 +08:00
width = vp.w;
2007-09-19 07:57:02 +08:00
}
2016-12-07 08:29:14 +08:00
width -= widget->border().width();
2007-09-19 07:57:02 +08:00
#endif
}
}
// Draw line-by-line
2016-11-22 23:47:46 +08:00
y = y1;
2007-09-19 07:57:02 +08:00
for (beg=end=text; end; ) {
2016-11-22 23:47:46 +08:00
x = x1;
2007-09-19 07:57:02 +08:00
// Without word-wrap
if (!(widget->align() & WORDWRAP)) {
end = std::strchr(beg, '\n');
2007-09-19 07:57:02 +08:00
if (end) {
chr = *end;
*end = 0;
2007-09-19 07:57:02 +08:00
}
}
// With word-wrap
2007-09-19 07:57:02 +08:00
else {
2020-04-09 04:48:06 +08:00
old_end = nullptr;
2007-09-19 07:57:02 +08:00
for (beg_end=beg;;) {
end = std::strpbrk(beg_end, " \n");
if (end) {
chr = *end;
*end = 0;
}
// To here we can print
2016-12-07 08:29:14 +08:00
if ((old_end) && (x+font->textLength(beg) > x1+width-scroll.x)) {
if (end)
*end = chr;
end = old_end;
chr = *end;
*end = 0;
break;
}
// We can print one word more
else if (end) {
// Force break
if (chr == '\n')
break;
*end = chr;
beg_end = end+1;
}
// We are in the end of text
else
break;
old_end = end;
2007-09-19 07:57:02 +08:00
}
}
len = font->textLength(beg);
2007-09-19 07:57:02 +08:00
// Render the text
2024-08-19 23:03:31 +08:00
if (g && len > 0) {
2007-09-19 07:57:02 +08:00
int xout;
if (widget->align() & CENTER)
xout = x + width/2 - len/2;
else if (widget->align() & RIGHT)
xout = x + width - len;
else // Left align
xout = x;
2007-09-19 07:57:02 +08:00
g->drawText(beg, fg, gfx::ColorNone, gfx::Point(xout, y));
2007-09-19 07:57:02 +08:00
}
if (w)
*w = std::max(*w, len);
2007-09-19 07:57:02 +08:00
y += textheight;
if (end) {
*end = chr;
beg = end+1;
}
}
if (h)
*h = (y - y1 + scroll.y);
2007-09-19 07:57:02 +08:00
if (w) *w += widget->border().width();
if (h) *h += widget->border().height();
2007-09-19 07:57:02 +08:00
}
} // namespace ui