Add canvas autoScaling feature (fix #3748)

This commit is contained in:
Martín Capello 2023-04-12 17:28:12 -03:00 committed by David Capello
parent 067309f776
commit 0958573cac
12 changed files with 280 additions and 70 deletions

View File

@ -18,18 +18,74 @@ namespace script {
namespace {
struct Theme { };
struct ThemeDimension { };
class Theme {
public:
Theme(int uiscale) : m_uiscale(uiscale) { }
gfx::Border styleMetrics(const std::string& id) const {
auto theme = skin::SkinTheme::instance();
if (!theme) return gfx::Border(0);
ui::Style* style = theme->getStyleById(id);
if (!style) return gfx::Border(0);
ui::Widget widget(ui::kGenericWidget);
auto border = theme->calcBorder(&widget, style);
if (m_uiscale > 1)
border /= m_uiscale;
return border;
}
int getDimensionById(const std::string& id) const {
int value = skin::SkinTheme::instance()->getDimensionById(id);
if (m_uiscale > 1)
value /= m_uiscale;
return value;
}
private:
int m_uiscale;
};
struct ThemeDimension {
Theme* theme;
ThemeDimension(Theme* theme) : theme(theme) { }
int getById(const std::string& id) const {
return theme->getDimensionById(id);
}
};
struct ThemeColor { };
#ifdef ENABLE_UI
void push_border_as_table(lua_State* L, const gfx::Border& border)
{
lua_newtable(L);
{
lua_newtable(L);
setfield_integer(L, "left", border.left());
setfield_integer(L, "top", border.top());
setfield_integer(L, "right", border.right());
setfield_integer(L, "bottom", border.bottom());
}
lua_setfield(L, -2, "border");
}
#endif
int ThemeDimension_index(lua_State* L)
{
[[maybe_unused]]
auto themeDimension = get_obj<ThemeDimension>(L, 1);
const char* id = lua_tostring(L, 2);
if (!id)
return luaL_error(L, "id in app.theme.dimension.id must be a string");
#ifdef ENABLE_UI
const int value = skin::SkinTheme::instance()->getDimensionById(id);
const int value = themeDimension->getById(id);
lua_pushinteger(L, value);
#else
lua_pushinteger(L, 0);
@ -55,38 +111,15 @@ int ThemeColor_index(lua_State* L)
int Theme_styleMetrics(lua_State* L)
{
[[maybe_unused]]
auto t = get_obj<Theme>(L, 1);
auto theme = get_obj<Theme>(L, 1);
const char* id = lua_tostring(L, 2);
if (!id)
return 0;
#ifdef ENABLE_UI
auto theme = skin::SkinTheme::instance();
if (!theme)
return 0;
ui::Style* style = theme->getStyleById(id);
if (!style)
return 0;
ui::Widget widget(ui::kGenericWidget);
gfx::Border border = theme->calcBorder(&widget, style);
lua_newtable(L);
{
lua_newtable(L);
lua_pushinteger(L, border.left());
lua_setfield(L, -2, "left");
lua_pushinteger(L, border.top());
lua_setfield(L, -2, "top");
lua_pushinteger(L, border.right());
lua_setfield(L, -2, "right");
lua_pushinteger(L, border.bottom());
lua_setfield(L, -2, "bottom");
}
lua_setfield(L, -2, "border");
gfx::Border border = theme->styleMetrics(id);
push_border_as_table(L, border);
return 1;
#else // ENABLE_UI
return 0;
@ -95,7 +128,8 @@ int Theme_styleMetrics(lua_State* L)
int Theme_get_dimension(lua_State* L)
{
push_obj<ThemeDimension>(L, ThemeDimension());
auto theme = get_obj<Theme>(L, 1);
push_new<ThemeDimension>(L, theme);
return 1;
}
@ -140,9 +174,9 @@ void register_theme_classes(lua_State* L)
REG_CLASS_PROPERTIES(L, Theme);
}
void push_app_theme(lua_State* L)
void push_app_theme(lua_State* L, int uiscale)
{
push_obj<Theme>(L, Theme());
push_new<Theme>(L, uiscale);
}
} // namespace script

View File

@ -52,8 +52,14 @@ void Canvas::callPaint()
m_surface->drawRect(m_surface->bounds(), p);
// Draw only on resize (onPaint we draw the cached m_surface)
GraphicsContext gc(m_surface);
gc.font(AddRef(font()));
GraphicsContext gc(m_surface, m_autoScaling ? ui::guiscale() : 1);
if (m_autoScaling) {
auto theme = skin::SkinTheme::get(this);
gc.font(AddRef(theme->getUnscaledFont(font())));
}
else
gc.font(AddRef(font()));
Paint(gc);
}
@ -69,6 +75,16 @@ void Canvas::onInitTheme(ui::InitThemeEvent& ev)
setBgColor(bg);
}
template <typename T>
static T makeScaled(T* msg, const gfx::Point& offset)
{
static_assert(std::is_base_of<ui::Message, T>::value, "type parameter must derive from ui::Message");
auto result = T(msg->type(), *msg, ((msg->position() - offset) / ui::guiscale()) + offset);
result.setRecipient(static_cast<ui::Message*>(msg)->recipient());
result.setDisplay(static_cast<ui::Message*>(msg)->display());
return result;
}
bool Canvas::onProcessMessage(ui::Message* msg)
{
switch (msg->type()) {
@ -98,8 +114,11 @@ bool Canvas::onProcessMessage(ui::Message* msg)
return true;
case ui::kMouseMoveMessage: {
auto mouseMsg = static_cast<ui::MouseMessage*>(msg);
MouseMove(mouseMsg);
auto mouseMsg = *static_cast<ui::MouseMessage*>(msg);
if (m_autoScaling) {
mouseMsg = makeScaled(&mouseMsg, bounds().origin());
}
MouseMove(&mouseMsg);
break;
}
@ -110,14 +129,20 @@ bool Canvas::onProcessMessage(ui::Message* msg)
if (isFocusStop() && !hasFocus())
requestFocus();
auto mouseMsg = static_cast<ui::MouseMessage*>(msg);
MouseDown(mouseMsg);
auto mouseMsg = *static_cast<ui::MouseMessage*>(msg);
if (m_autoScaling) {
mouseMsg = makeScaled(&mouseMsg, bounds().origin());
}
MouseDown(&mouseMsg);
break;
}
case ui::kMouseUpMessage: {
auto mouseMsg = static_cast<ui::MouseMessage*>(msg);
MouseUp(mouseMsg);
auto mouseMsg = *static_cast<ui::MouseMessage*>(msg);
if (m_autoScaling) {
mouseMsg = makeScaled(&mouseMsg, bounds().origin());
}
MouseUp(&mouseMsg);
if (hasCapture())
releaseMouse();
@ -125,20 +150,29 @@ bool Canvas::onProcessMessage(ui::Message* msg)
}
case ui::kDoubleClickMessage: {
auto mouseMsg = static_cast<ui::MouseMessage*>(msg);
DoubleClick(mouseMsg);
auto mouseMsg = *static_cast<ui::MouseMessage*>(msg);
if (m_autoScaling) {
mouseMsg = makeScaled(&mouseMsg, bounds().origin());
}
DoubleClick(&mouseMsg);
break;
}
case ui::kMouseWheelMessage: {
auto mouseMsg = static_cast<ui::MouseMessage*>(msg);
Wheel(mouseMsg);
auto mouseMsg = *static_cast<ui::MouseMessage*>(msg);
if (m_autoScaling) {
mouseMsg = makeScaled(&mouseMsg, bounds().origin());
}
Wheel(&mouseMsg);
break;
}
case ui::kTouchMagnifyMessage: {
auto touchMsg = static_cast<ui::TouchMessage*>(msg);
TouchMagnify(touchMsg);
auto touchMsg = *static_cast<ui::TouchMessage*>(msg);
if (m_autoScaling) {
touchMsg = makeScaled(&touchMsg, bounds().origin());
}
TouchMagnify(&touchMsg);
break;
}
@ -150,14 +184,18 @@ void Canvas::onResize(ui::ResizeEvent& ev)
{
Widget::onResize(ev);
if (os::instance() && !ev.bounds().isEmpty()) {
const int w = ev.bounds().w;
const int h = ev.bounds().h;
int w = ev.bounds().w;
int h = ev.bounds().h;
if (m_autoScaling) {
w = w/ui::guiscale() + (w % ui::guiscale());
h = h/ui::guiscale() + (h % ui::guiscale());
}
if (!m_surface ||
m_surface->width() != w ||
m_surface->height() != h) {
m_surface = os::instance()->makeSurface(w, h);
callPaint();
}
}
@ -168,9 +206,14 @@ void Canvas::onResize(ui::ResizeEvent& ev)
void Canvas::onPaint(ui::PaintEvent& ev)
{
auto g = ev.graphics();
const gfx::Rect rc = clientBounds();
if (m_surface)
g->drawSurface(m_surface.get(), rc.x, rc.y);
gfx::Rect rc = clientBounds();
if (m_surface) {
if (m_autoScaling) {
rc.w += (rc.w % ui::guiscale());
rc.h += (rc.h % ui::guiscale());
}
g->drawSurface(m_surface.get(), m_surface->bounds(), rc, os::Sampling(), nullptr);
}
}
} // namespace script

View File

@ -35,6 +35,14 @@ public:
m_cursorType = cursor;
}
void setAutoScaling(const bool v) {
m_autoScaling = v;
}
bool isAutoScaling() const {
return m_autoScaling;
}
obs::signal<void(GraphicsContext&)> Paint;
obs::signal<void(ui::KeyMessage*)> KeyDown;
obs::signal<void(ui::KeyMessage*)> KeyUp;
@ -57,6 +65,11 @@ private:
os::SurfaceRef m_surface;
ui::CursorType m_cursorType = ui::kArrowCursor;
// Flag used to indicate that the canvas will scale all the drawing operations
// according to the UI scale's preferences setting. So the user doesn't have to
// take care about the current scale when writing scripts.
bool m_autoScaling = true;
};
} // namespace script

View File

@ -35,6 +35,7 @@
#include "ui/manager.h"
#include "ui/menu.h"
#include "ui/message.h"
#include "ui/scale.h"
#include "ui/separator.h"
#include "ui/slider.h"
#include "ui/system.h"
@ -1103,6 +1104,15 @@ int Dialog_canvas(lua_State* L)
}
lua_pop(L, 1);
type = lua_getfield(L, 2, "autoScaling");
if (type != LUA_TNIL) {
widget->setAutoScaling(lua_toboolean(L, -1));
}
lua_pop(L, 1);
if (widget->isAutoScaling())
sz *= ui::guiscale();
widget->setSizeHint(sz);
bool handleKeyEvents = false;

View File

@ -139,7 +139,7 @@ namespace app {
};
void push_app_events(lua_State* L);
void push_app_theme(lua_State* L);
void push_app_theme(lua_State* L, int uiscale = 1);
int push_image_iterator_function(lua_State* L, const doc::Image* image, int extraArgIndex);
void push_brush(lua_State* L, const doc::BrushRef& brush);
void push_cel_image(lua_State* L, doc::Cel* cel);

View File

@ -84,16 +84,20 @@ void GraphicsContext::drawImage(const doc::Image* img,
void GraphicsContext::drawThemeImage(const std::string& partId, const gfx::Point& pt)
{
if (auto theme = skin::SkinTheme::instance()) {
skin::SkinPartPtr part = theme->getPartById(partId);
if (part && part->bitmap(0))
m_surface->drawRgbaSurface(part->bitmap(0), pt.x, pt.y);
skin::SkinPartPtr part = (m_uiscale > 1 ? theme->getUnscaledPartById(partId):
theme->getPartById(partId));
if (part && part->bitmap(0)) {
auto bmp = part->bitmap(0);
m_surface->drawRgbaSurface(bmp, pt.x, pt.y);
}
}
}
void GraphicsContext::drawThemeRect(const std::string& partId, const gfx::Rect& rc)
{
if (auto theme = skin::SkinTheme::instance()) {
skin::SkinPartPtr part = theme->getPartById(partId);
skin::SkinPartPtr part = (m_uiscale > 1 ? theme->getUnscaledPartById(partId):
theme->getPartById(partId));
if (part && part->bitmap(0)) {
ui::Graphics g(nullptr, m_surface, 0, 0);
@ -101,16 +105,20 @@ void GraphicsContext::drawThemeRect(const std::string& partId, const gfx::Rect&
// 9-slices
if (!part->slicesBounds().isEmpty()) {
theme->drawRect(&g, rc, part.get(), true);
if (m_uiscale > 1)
theme->drawRectUsingUnscaledSheet(&g, rc, part.get(), true);
else
theme->drawRect(&g, rc, part.get(), true);
}
else {
ui::IntersectClip clip(&g, rc);
if (clip) {
auto bmp = part->bitmap(0);
// Horizontal line
if (rc.w > part->spriteBounds().w) {
for (int x=rc.x; x<rc.x2(); x+=part->spriteBounds().w) {
g.drawRgbaSurface(
part->bitmap(0),
bmp,
x, rc.y+rc.h/2-part->spriteBounds().h/2);
}
}
@ -118,7 +126,7 @@ void GraphicsContext::drawThemeRect(const std::string& partId, const gfx::Rect&
else {
for (int y=rc.y; y<rc.y2(); y+=part->spriteBounds().h) {
g.drawRgbaSurface(
part->bitmap(0),
bmp,
rc.x+rc.w/2-part->spriteBounds().w/2, y);
}
}
@ -236,6 +244,13 @@ int GraphicsContext_drawImage(lua_State* L)
return 0;
}
int GraphicsContext_theme(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
push_app_theme(L, gc->uiscale());
return 1;
}
int GraphicsContext_drawThemeImage(lua_State* L)
{
auto gc = get_obj<GraphicsContext>(L, 1);
@ -466,6 +481,7 @@ const luaL_Reg GraphicsContext_methods[] = {
const Property GraphicsContext_properties[] = {
{ "width", GraphicsContext_get_width, nullptr },
{ "height", GraphicsContext_get_height, nullptr },
{ "theme", GraphicsContext_theme, nullptr },
{ "antialias", GraphicsContext_get_antialias, GraphicsContext_set_antialias },
{ "color", GraphicsContext_get_color, GraphicsContext_set_color },
{ "strokeWidth", GraphicsContext_get_strokeWidth, GraphicsContext_set_strokeWidth },

View File

@ -26,12 +26,13 @@ namespace script {
class GraphicsContext {
public:
GraphicsContext(const os::SurfaceRef& surface) : m_surface(surface) { }
GraphicsContext(const os::SurfaceRef& surface, int uiscale) : m_surface(surface), m_uiscale(uiscale) { }
GraphicsContext(GraphicsContext&& gc) {
std::swap(m_surface, gc.m_surface);
std::swap(m_paint, gc.m_paint);
std::swap(m_font, gc.m_font);
std::swap(m_path, gc.m_path);
m_uiscale = gc.m_uiscale;
}
os::FontRef font() const { return m_font; }
@ -118,8 +119,14 @@ public:
m_surface->clipPath(m_path);
}
int uiscale() const {
return m_uiscale;
}
private:
os::SurfaceRef m_surface = nullptr;
// Keeps the UI Scale currently in use when canvas autoScaling is enabled.
int m_uiscale;
os::Paint m_paint;
os::FontRef m_font;
gfx::Path m_path;

View File

@ -28,13 +28,13 @@ FontData::FontData(os::FontType type)
{
}
os::FontRef FontData::getFont(int size)
os::FontRef FontData::getFont(int size, int uiscale)
{
if (m_type == os::FontType::SpriteSheet)
if (m_type == os::FontType::SpriteSheet)
size = 1; // Same size always
// Use cache
size *= ui::guiscale();
size *= uiscale;
auto it = m_fonts.find(size);
if (it != m_fonts.end())
return it->second;
@ -66,5 +66,10 @@ os::FontRef FontData::getFont(int size)
return font;
}
os::FontRef FontData::getFont(int size)
{
return getFont(size, ui::guiscale());
}
} // namespace skin
} // namespace app

View File

@ -28,6 +28,7 @@ namespace skin {
m_fallbackSize = fallbackSize;
}
os::FontRef getFont(int size, int uiscale);
os::FontRef getFont(int size);
private:

View File

@ -288,6 +288,7 @@ SkinTheme::~SkinTheme()
for (auto& it : m_cursors)
delete it.second; // Delete cursor
m_unscaledSheet.reset();
m_sheet.reset();
m_parts_by_id.clear();
@ -337,12 +338,12 @@ void SkinTheme::loadFontData()
{
LOG("THEME: Loading fonts\n");
std::string fonstFilename("fonts/fonts.xml");
std::string fontsFilename("fonts/fonts.xml");
ResourceFinder rf;
rf.includeDataDir(fonstFilename.c_str());
rf.includeDataDir(fontsFilename.c_str());
if (!rf.findFirst())
throw base::Exception("File %s not found", fonstFilename.c_str());
throw base::Exception("File %s not found", fontsFilename.c_str());
XmlDocumentRef doc = open_xml(rf.filename());
TiXmlHandle handle(doc.get());
@ -387,6 +388,12 @@ void SkinTheme::loadSheet()
if (!newSheet)
throw base::Exception("Error loading %s file", sheet_filename.c_str());
// TODO Change os::Surface::applyScale() to return a new surface,
// avoid loading two times the same file (even more, if there
// is no scale to apply, m_unscaledSheet must reference the
// same m_sheet).
m_unscaledSheet = os::instance()->loadRgbaSurface(sheet_filename.c_str());
// Replace the sprite sheet
if (m_sheet)
m_sheet.reset();
@ -456,6 +463,10 @@ void SkinTheme::loadXml(BackwardCompatibility* backward)
os::FontRef font = fontData->getFont(size);
m_themeFonts[idStr] = ThemeFont(font, mnemonics);
// Store a unscaled version for using when ui scaling is not desired (i.e. in a Canvas widget with
// autoScaling enabled).
m_unscaledFonts[font.get()] = fontData->getFont(size, 1);
if (id == "default")
m_defaultFont = font;
else if (id == "mini")
@ -530,9 +541,15 @@ void SkinTheme::loadXml(BackwardCompatibility* backward)
if (!part)
part = m_parts_by_id[part_id] = SkinPartPtr(new SkinPart);
SkinPartPtr unscaledPart = m_unscaledParts_by_id[part_id];
if (!unscaledPart)
unscaledPart = m_unscaledParts_by_id[part_id] = SkinPartPtr(new SkinPart);
if (w > 0 && h > 0) {
part->setSpriteBounds(gfx::Rect(x, y, w, h));
part->setBitmap(0, sliceSheet(part->bitmapRef(0), gfx::Rect(x, y, w, h)));
unscaledPart->setSpriteBounds(part->spriteBounds()/scale);
unscaledPart->setBitmap(0, sliceUnscaledSheet(unscaledPart->bitmapRef(0), unscaledPart->spriteBounds()));
}
else if (xmlPart->Attribute("w1")) { // 3x3-1 part (NW, N, NE, E, SE, S, SW, W)
int w1 = scale*strtol(xmlPart->Attribute("w1"), nullptr, 10);
@ -553,6 +570,18 @@ void SkinTheme::loadXml(BackwardCompatibility* backward)
part->setBitmap(5, sliceSheet(part->bitmapRef(5), gfx::Rect(x+w1, y+h1+h2, w2, h3))); // S
part->setBitmap(6, sliceSheet(part->bitmapRef(6), gfx::Rect(x, y+h1+h2, w1, h3))); // SW
part->setBitmap(7, sliceSheet(part->bitmapRef(7), gfx::Rect(x, y+h1, w1, h2))); // W
unscaledPart->setSpriteBounds(part->spriteBounds()/scale);
unscaledPart->setSlicesBounds(part->slicesBounds()/scale);
unscaledPart->setBitmap(0, sliceUnscaledSheet(unscaledPart->bitmapRef(0), gfx::Rect(x, y, w1, h1)/scale));
unscaledPart->setBitmap(1, sliceUnscaledSheet(unscaledPart->bitmapRef(1), gfx::Rect(x+w1, y, w2, h1)/scale));
unscaledPart->setBitmap(2, sliceUnscaledSheet(unscaledPart->bitmapRef(2), gfx::Rect(x+w1+w2, y, w3, h1)/scale));
unscaledPart->setBitmap(3, sliceUnscaledSheet(unscaledPart->bitmapRef(3), gfx::Rect(x+w1+w2, y+h1, w3, h2)/scale));
unscaledPart->setBitmap(4, sliceUnscaledSheet(unscaledPart->bitmapRef(4), gfx::Rect(x+w1+w2, y+h1+h2, w3, h3)/scale));
unscaledPart->setBitmap(5, sliceUnscaledSheet(unscaledPart->bitmapRef(5), gfx::Rect(x+w1, y+h1+h2, w2, h3)/scale));
unscaledPart->setBitmap(6, sliceUnscaledSheet(unscaledPart->bitmapRef(6), gfx::Rect(x, y+h1+h2, w1, h3)/scale));
unscaledPart->setBitmap(7, sliceUnscaledSheet(unscaledPart->bitmapRef(7), gfx::Rect(x, y+h1, w1, h2)/scale));
}
// Is it a mouse cursor?
@ -847,7 +876,7 @@ void SkinTheme::loadXml(BackwardCompatibility* backward)
ThemeFile<SkinTheme>::updateInternals();
}
os::SurfaceRef SkinTheme::sliceSheet(os::SurfaceRef sur, const gfx::Rect& bounds)
static os::SurfaceRef sliceSheet(os::SurfaceRef sheet, os::SurfaceRef sur, const gfx::Rect& bounds)
{
if (sur && (sur->width() != bounds.w ||
sur->height() != bounds.h)) {
@ -858,9 +887,9 @@ os::SurfaceRef SkinTheme::sliceSheet(os::SurfaceRef sur, const gfx::Rect& bounds
if (!sur)
sur = os::instance()->makeRgbaSurface(bounds.w, bounds.h);
os::SurfaceLock lockSrc(m_sheet.get());
os::SurfaceLock lockSrc(sheet.get());
os::SurfaceLock lockDst(sur.get());
m_sheet->blitTo(sur.get(), bounds.x, bounds.y, 0, 0, bounds.w, bounds.h);
sheet->blitTo(sur.get(), bounds.x, bounds.y, 0, 0, bounds.w, bounds.h);
// The new surface is immutable because we're going to re-use the
// surface if we reload the theme.
@ -875,6 +904,16 @@ os::SurfaceRef SkinTheme::sliceSheet(os::SurfaceRef sur, const gfx::Rect& bounds
return sur;
}
os::SurfaceRef SkinTheme::sliceSheet(os::SurfaceRef sur, const gfx::Rect& bounds)
{
return app::skin::sliceSheet(m_sheet, sur, bounds);
}
os::SurfaceRef SkinTheme::sliceUnscaledSheet(os::SurfaceRef sur, const gfx::Rect& bounds)
{
return app::skin::sliceSheet(m_unscaledSheet, sur, bounds);
}
os::Font* SkinTheme::getWidgetFont(const Widget* widget) const
{
auto skinPropery = std::static_pointer_cast<SkinProperty>(widget->getProperty(SkinProperty::Name));
@ -1712,6 +1751,16 @@ void SkinTheme::drawRect(ui::Graphics* g, const gfx::Rect& rc,
drawCenter);
}
void SkinTheme::drawRectUsingUnscaledSheet(ui::Graphics* g, const gfx::Rect& rc,
SkinPart* skinPart, const bool drawCenter)
{
Theme::drawSlices(g, m_unscaledSheet.get(), rc,
skinPart->spriteBounds(),
skinPart->slicesBounds(),
gfx::ColorNone,
drawCenter);
}
void SkinTheme::drawRect2(Graphics* g, const Rect& rc, int x_mid,
SkinPart* nw1, SkinPart* nw2)
{

View File

@ -65,6 +65,13 @@ namespace app {
os::Font* getDefaultFont() const override { return m_defaultFont.get(); }
os::Font* getWidgetFont(const ui::Widget* widget) const override;
os::Font* getMiniFont() const { return m_miniFont.get(); }
os::Font* getUnscaledFont(os::Font* font) const {
auto it = m_unscaledFonts.find(font);
if (it != m_unscaledFonts.end())
return it->second.get();
else
return font;
}
ui::Cursor* getStandardCursor(ui::CursorType type) override;
void initWidget(ui::Widget* widget) override;
@ -90,6 +97,7 @@ namespace app {
os::Surface* e, os::Surface* se, os::Surface* s,
os::Surface* sw, os::Surface* w);
void drawRect(ui::Graphics* g, const gfx::Rect& rc, SkinPart* skinPart, const bool drawCenter = true);
void drawRectUsingUnscaledSheet(ui::Graphics* g, const gfx::Rect& rc, SkinPart* skinPart, const bool drawCenter = true);
void drawRect2(ui::Graphics* g, const gfx::Rect& rc, int x_mid, SkinPart* nw1, SkinPart* nw2);
void drawHline(ui::Graphics* g, const gfx::Rect& rc, SkinPart* skinPart);
void drawVline(ui::Graphics* g, const gfx::Rect& rc, SkinPart* skinPart);
@ -111,6 +119,14 @@ namespace app {
return SkinPartPtr(nullptr);
}
SkinPartPtr getUnscaledPartById(const std::string& id) const {
auto it = m_unscaledParts_by_id.find(id);
if (it != m_unscaledParts_by_id.end())
return it->second;
else
return SkinPartPtr(nullptr);
}
ui::Cursor* getCursorById(const std::string& id) const {
auto it = m_cursors.find(id);
if (it != m_cursors.end())
@ -150,6 +166,7 @@ namespace app {
void loadXml(BackwardCompatibility* backward);
os::SurfaceRef sliceSheet(os::SurfaceRef sur, const gfx::Rect& bounds);
os::SurfaceRef sliceUnscaledSheet(os::SurfaceRef sur, const gfx::Rect& bounds);
gfx::Color getWidgetBgColor(ui::Widget* widget);
void drawText(ui::Graphics* g,
const char* t,
@ -165,7 +182,11 @@ namespace app {
std::string m_path;
os::SurfaceRef m_sheet;
// Contains the sheet surface as is, without any scale.
os::SurfaceRef m_unscaledSheet;
std::map<std::string, SkinPartPtr> m_parts_by_id;
// Stores the same SkinParts as m_parts_by_id but unscaled, using the same keys.
std::map<std::string, SkinPartPtr> m_unscaledParts_by_id;
std::map<std::string, gfx::Color> m_colors_by_id;
std::map<std::string, int> m_dimensions_by_id;
std::map<std::string, ui::Cursor*> m_cursors;
@ -173,6 +194,8 @@ namespace app {
std::map<std::string, ui::Style*> m_styles;
std::map<std::string, FontData*> m_fonts;
std::map<std::string, ThemeFont> m_themeFonts;
// Stores the unscaled font version of the Font pointer used as a key.
std::map<os::Font*, os::FontRef> m_unscaledFonts;
os::FontRef m_defaultFont;
os::FontRef m_miniFont;
int m_preferredScreenScaling;

View File

@ -188,6 +188,15 @@ namespace ui {
m_magnification(magnification) {
}
// Copy other TouchMessage converting its type
TouchMessage(MessageType type,
const TouchMessage& other,
const gfx::Point& newPosition)
: Message(type, other.modifiers()),
m_pos(newPosition),
m_magnification(other.magnification()) {
}
const gfx::Point& position() const { return m_pos; }
double magnification() const { return m_magnification; }