2013-08-09 08:01:20 +08:00
|
|
|
// Aseprite UI Library
|
2017-02-07 04:58:55 +08:00
|
|
|
// Copyright (C) 2001-2017 David Capello
|
2010-09-28 06:18:17 +08:00
|
|
|
//
|
2014-03-30 07:08:05 +08:00
|
|
|
// 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
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2009-07-13 04:29:16 +08:00
|
|
|
#include "config.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#endif
|
2009-07-13 04:29:16 +08:00
|
|
|
|
2011-02-21 05:35:21 +08:00
|
|
|
#include "gfx/point.h"
|
|
|
|
#include "gfx/size.h"
|
2014-06-23 05:53:14 +08:00
|
|
|
#include "she/font.h"
|
2017-02-10 03:51:52 +08:00
|
|
|
#include "she/surface.h"
|
2014-06-23 05:53:14 +08:00
|
|
|
#include "she/system.h"
|
2012-06-18 09:49:58 +08:00
|
|
|
#include "ui/intern.h"
|
|
|
|
#include "ui/manager.h"
|
|
|
|
#include "ui/system.h"
|
|
|
|
#include "ui/theme.h"
|
|
|
|
#include "ui/view.h"
|
|
|
|
#include "ui/widget.h"
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2015-03-05 08:35:11 +08:00
|
|
|
#include <cstring>
|
|
|
|
|
2012-06-18 09:02:54 +08:00
|
|
|
namespace ui {
|
|
|
|
|
2011-01-22 04:50:04 +08:00
|
|
|
static Theme* current_theme = NULL;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-01-22 04:50:04 +08:00
|
|
|
Theme::Theme()
|
2014-11-26 09:33:45 +08:00
|
|
|
: m_guiscale(1)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-01-22 04:50:04 +08:00
|
|
|
Theme::~Theme()
|
|
|
|
{
|
|
|
|
if (current_theme == this)
|
2016-12-12 20:48:58 +08:00
|
|
|
set_theme(nullptr);
|
2011-01-22 04:50:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Theme::regenerate()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2014-11-26 08:30:56 +08:00
|
|
|
CursorType type = get_mouse_cursor();
|
|
|
|
set_mouse_cursor(kNoCursor);
|
2011-01-22 04:50:04 +08:00
|
|
|
|
|
|
|
onRegenerate();
|
|
|
|
|
2015-09-01 21:18:47 +08:00
|
|
|
details::resetFontAllWidgets();
|
2015-04-17 23:24:33 +08:00
|
|
|
|
|
|
|
// TODO We cannot reinitialize all widgets because this mess all
|
|
|
|
// child spacing, border, etc. But it could be good to change the
|
|
|
|
// uiscale() and get the new look without the need to restart the
|
|
|
|
// whole app.
|
2015-09-01 21:18:47 +08:00
|
|
|
//details::reinitThemeForAllWidgets();
|
2015-04-17 23:24:33 +08:00
|
|
|
|
2014-11-26 08:30:56 +08:00
|
|
|
set_mouse_cursor(type);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2011-01-22 04:50:04 +08:00
|
|
|
//////////////////////////////////////////////////////////////////////
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2016-12-12 20:48:58 +08:00
|
|
|
void set_theme(Theme* theme)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2016-10-27 21:44:31 +08:00
|
|
|
if (theme) {
|
|
|
|
// As the regeneration may fail, first we regenerate the theme and
|
|
|
|
// then we set is as "the current theme." E.g. In case that we'd
|
|
|
|
// like to show some kind of error message with the UI controls,
|
|
|
|
// we should be able to use the previous theme to do so (instead
|
|
|
|
// of this new unsuccessfully regenerated theme).
|
|
|
|
theme->regenerate();
|
|
|
|
|
|
|
|
current_theme = theme;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-06-18 09:02:54 +08:00
|
|
|
Manager* manager = Manager::getDefault();
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
if (manager && !manager->theme())
|
2011-01-22 05:08:25 +08:00
|
|
|
manager->setTheme(theme);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-12 20:48:58 +08:00
|
|
|
Theme* get_theme()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2011-01-22 04:50:04 +08:00
|
|
|
return current_theme;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2016-12-15 21:50:47 +08:00
|
|
|
// static
|
2017-02-10 03:51:52 +08:00
|
|
|
void Theme::drawSlices(Graphics* g, she::Surface* sheet,
|
|
|
|
const gfx::Rect& rc,
|
|
|
|
const gfx::Rect& sprite,
|
|
|
|
const gfx::Rect& slices,
|
|
|
|
const bool drawCenter)
|
|
|
|
{
|
|
|
|
const int w1 = slices.x;
|
|
|
|
const int h1 = slices.y;
|
|
|
|
const int w2 = slices.w;
|
|
|
|
const int h2 = slices.h;
|
|
|
|
const int w3 = sprite.w-w1-w2;
|
|
|
|
const int h3 = sprite.h-h1-h2;
|
|
|
|
const int x2 = rc.x2()-w3;
|
|
|
|
const int y2 = rc.y2()-h3;
|
|
|
|
|
|
|
|
// Top
|
|
|
|
int x = rc.x;
|
|
|
|
int y = rc.y;
|
|
|
|
g->drawRgbaSurface(sheet, sprite.x, sprite.y,
|
|
|
|
x, y, w1, h1);
|
|
|
|
{
|
|
|
|
IntersectClip clip(g, gfx::Rect(rc.x+w1, rc.y, rc.w-w1-w3, h1));
|
|
|
|
if (clip) {
|
|
|
|
for (x+=w1; x<x2; x+=w2) {
|
|
|
|
g->drawRgbaSurface(sheet, sprite.x+w1, sprite.y,
|
|
|
|
x, y, w2, h1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g->drawRgbaSurface(sheet, sprite.x+w1+w2, sprite.y,
|
|
|
|
x2, y, w3, h1);
|
|
|
|
|
|
|
|
// Bottom
|
|
|
|
x = rc.x;
|
|
|
|
y = y2;
|
|
|
|
g->drawRgbaSurface(sheet, sprite.x, sprite.y+h1+h2,
|
|
|
|
x, y, w1, h3);
|
|
|
|
{
|
|
|
|
IntersectClip clip(g, gfx::Rect(rc.x+w1, y2, rc.w-w1-w3, h3));
|
|
|
|
if (clip) {
|
|
|
|
for (x+=w1; x<x2; x+=w2) {
|
|
|
|
g->drawRgbaSurface(sheet, sprite.x+w1, sprite.y+h1+h2,
|
|
|
|
x, y2, w2, h3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g->drawRgbaSurface(sheet, sprite.x+w1+w2, sprite.y+h1+h2,
|
|
|
|
x2, y2, w3, h3);
|
|
|
|
|
|
|
|
// Left & Right
|
|
|
|
IntersectClip clip(g, gfx::Rect(rc.x, rc.y+h1, rc.w, rc.h-h1-h3));
|
|
|
|
if (clip) {
|
|
|
|
for (y=rc.y+h1; y<y2; y+=h2) {
|
|
|
|
// Left
|
|
|
|
g->drawRgbaSurface(sheet, sprite.x, sprite.y+h1,
|
|
|
|
rc.x, y, w1, h2);
|
|
|
|
// Right
|
|
|
|
g->drawRgbaSurface(sheet, sprite.x+w1+w2, sprite.y+h1,
|
|
|
|
x2, y, w3, h2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Center
|
|
|
|
if (drawCenter) {
|
|
|
|
IntersectClip clip(g, gfx::Rect(rc.x+w1, rc.y+h1, rc.w-w1-w3, rc.h-h1-h3));
|
|
|
|
if (clip) {
|
|
|
|
for (y=rc.y+h1; y<y2; y+=h2) {
|
|
|
|
for (x=rc.x+w1; x<x2; x+=w2)
|
|
|
|
g->drawRgbaSurface(sheet, sprite.x+w1, sprite.y+h1, x, y, w2, h2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
2016-12-15 21:50:47 +08:00
|
|
|
void Theme::drawTextBox(Graphics* g, Widget* widget,
|
|
|
|
int* w, int* h, gfx::Color bg, gfx::Color fg)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2011-02-21 05:35:21 +08:00
|
|
|
View* view = View::getView(widget);
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
char* text = const_cast<char*>(widget->text().c_str());
|
2013-10-15 06:58:11 +08:00
|
|
|
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;
|
2011-02-21 05:35:21 +08:00
|
|
|
gfx::Point scroll;
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
int textheight = widget->textHeight();
|
|
|
|
she::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());
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
scroll = view->viewScroll();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
else {
|
2016-11-22 23:47:46 +08:00
|
|
|
vp = widget->clientBounds();
|
2011-02-21 05:35:21 +08:00
|
|
|
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;
|
|
|
|
|
2014-04-20 02:18:16 +08:00
|
|
|
// Without word-wrap
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
if (!(widget->align() & WORDWRAP)) {
|
2016-12-07 08:29:14 +08:00
|
|
|
width = widget->clientChildrenBounds().w;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2014-04-20 02:18:16 +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) {
|
2012-01-06 06:45:03 +08:00
|
|
|
gfx::Size maxSize = view->getScrollableSize();
|
2016-11-22 23:47:46 +08:00
|
|
|
width = 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-20 02:18:16 +08:00
|
|
|
// 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
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
// Without word-wrap
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
if (!(widget->align() & WORDWRAP)) {
|
2015-03-05 08:35:11 +08:00
|
|
|
end = std::strchr(beg, '\n');
|
2007-09-19 07:57:02 +08:00
|
|
|
if (end) {
|
2012-01-06 06:45:03 +08:00
|
|
|
chr = *end;
|
|
|
|
*end = 0;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
}
|
2013-10-15 06:58:11 +08:00
|
|
|
// With word-wrap
|
2007-09-19 07:57:02 +08:00
|
|
|
else {
|
|
|
|
old_end = NULL;
|
|
|
|
for (beg_end=beg;;) {
|
2015-03-05 08:35:11 +08:00
|
|
|
end = std::strpbrk(beg_end, " \n");
|
2012-01-06 06:45:03 +08:00
|
|
|
if (end) {
|
|
|
|
chr = *end;
|
|
|
|
*end = 0;
|
|
|
|
}
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
// To here we can print
|
2016-12-07 08:29:14 +08:00
|
|
|
if ((old_end) && (x+font->textLength(beg) > x1+width-scroll.x)) {
|
2012-01-06 06:45:03 +08:00
|
|
|
if (end)
|
|
|
|
*end = chr;
|
|
|
|
|
|
|
|
end = old_end;
|
|
|
|
chr = *end;
|
|
|
|
*end = 0;
|
|
|
|
break;
|
|
|
|
}
|
2013-10-15 06:58:11 +08:00
|
|
|
// We can print one word more
|
2012-01-06 06:45:03 +08:00
|
|
|
else if (end) {
|
2013-10-15 06:58:11 +08:00
|
|
|
// Force break
|
2012-01-06 06:45:03 +08:00
|
|
|
if (chr == '\n')
|
|
|
|
break;
|
|
|
|
|
|
|
|
*end = chr;
|
|
|
|
beg_end = end+1;
|
|
|
|
}
|
2013-10-15 06:58:11 +08:00
|
|
|
// We are in the end of text
|
2012-01-06 06:45:03 +08:00
|
|
|
else
|
|
|
|
break;
|
|
|
|
|
|
|
|
old_end = end;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-23 05:53:14 +08:00
|
|
|
len = font->textLength(beg);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2014-04-20 02:18:16 +08:00
|
|
|
// Render the text
|
|
|
|
if (g) {
|
2007-09-19 07:57:02 +08:00
|
|
|
int xout;
|
|
|
|
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
if (widget->align() & CENTER)
|
2012-01-06 06:45:03 +08:00
|
|
|
xout = x + width/2 - len/2;
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
else if (widget->align() & RIGHT)
|
2012-01-06 06:45:03 +08:00
|
|
|
xout = x + width - len;
|
2014-04-20 02:18:16 +08:00
|
|
|
else // Left align
|
2012-01-06 06:45:03 +08:00
|
|
|
xout = x;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2017-02-07 04:58:55 +08:00
|
|
|
g->drawText(beg, fg, gfx::ColorNone, gfx::Point(xout, y));
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (w)
|
2008-01-07 23:10:17 +08:00
|
|
|
*w = MAX(*w, len);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
|
|
|
y += textheight;
|
|
|
|
|
|
|
|
if (end) {
|
|
|
|
*end = chr;
|
|
|
|
beg = end+1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (h)
|
2014-04-20 02:18:16 +08:00
|
|
|
*h = (y - y1 + scroll.y);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2015-06-24 06:20:49 +08:00
|
|
|
if (w) *w += widget->border().width();
|
|
|
|
if (h) *h += widget->border().height();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2012-06-18 09:02:54 +08:00
|
|
|
|
|
|
|
} // namespace ui
|