Add ui::Display to support multiple windows in the future

This ui::Display is related to one native os::Window, so the dirty
region/invalid region is now part of a ui::Display instead of a
ui::Manager.

* Replaced ui::display_w/h() functions with ui::Display::size()
  and ui::get_desktop_size()
* The ui::Manager contains the main ui::Display, and in the future an
  ui::Window will have its own ui::Display
This commit is contained in:
David Capello 2021-02-18 12:30:14 -03:00
parent 715600679d
commit c3d52f0bbe
56 changed files with 759 additions and 401 deletions

2
laf

@ -1 +1 @@
Subproject commit 0ae7eb7c7fd40c5a3829cfddaede60f458877920
Subproject commit 410da165bde1e7353eb46aa58481a956f2ad489b

View File

@ -359,7 +359,7 @@ void App::run()
if (isGui()) {
#ifdef _WIN32
// How to interpret one finger on Windows tablets.
ui::Manager::getDefault()->nativeWindow()
ui::Manager::getDefault()->display()->nativeWindow()
->setInterpretOneFingerGestureAsMouseMovement(
preferences().experimental.oneFingerAsMouseMovement());
#endif

View File

@ -43,7 +43,7 @@ void FullscreenModeCommand::onExecute(Context* ctx)
if (!manager)
return;
os::Window* window = manager->nativeWindow();
os::Window* window = manager->display()->nativeWindow();
ASSERT(window);
if (!window)
return;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -51,8 +51,13 @@ public:
, m_pal(m_sprite->palette(editor->frame()))
, m_proj(editor->projection())
, m_index_bg_color(-1)
, m_doublebuf(Image::create(IMAGE_RGB, ui::display_w(), ui::display_h()))
, m_doublesur(os::instance()->makeRgbaSurface(ui::display_w(), ui::display_h())) {
, m_doublebuf(Image::create(
IMAGE_RGB,
editor->display()->size().w,
editor->display()->size().h))
, m_doublesur(os::instance()->makeRgbaSurface(
editor->display()->size().w,
editor->display()->size().h)) {
// Do not use DocWriter (do not lock the document) because we
// will call other sub-commands (e.g. previous frame, next frame,
// etc.).
@ -170,6 +175,7 @@ protected:
}
virtual void onPaint(PaintEvent& ev) override {
gfx::Size displaySize = display()->size();
Graphics* g = ev.graphics();
EditorRender& render = m_editor->renderEngine();
render.setRefLayersVisiblity(false);
@ -213,18 +219,18 @@ protected:
255, BlendMode::NORMAL);
break;
case TiledMode::X_AXIS:
for (u=x-w; u<ui::display_w()+w; u+=w)
for (u=x-w; u<displaySize.w+w; u+=w)
render.renderImage(m_doublebuf.get(), m_render.get(), m_pal, u, y,
255, BlendMode::NORMAL);
break;
case TiledMode::Y_AXIS:
for (v=y-h; v<ui::display_h()+h; v+=h)
for (v=y-h; v<displaySize.h+h; v+=h)
render.renderImage(m_doublebuf.get(), m_render.get(), m_pal, x, v,
255, BlendMode::NORMAL);
break;
case TiledMode::BOTH:
for (v=y-h; v<ui::display_h()+h; v+=h)
for (u=x-w; u<ui::display_w()+w; u+=w)
for (v=y-h; v<displaySize.h+h; v+=h)
for (u=x-w; u<displaySize.w+w; u+=w)
render.renderImage(m_doublebuf.get(), m_render.get(), m_pal, u, v,
255, BlendMode::NORMAL);
break;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -88,8 +88,9 @@ public:
m_keyLabel.setStyle(theme->styles.listHeaderLabel());
m_contextLabel.setStyle(theme->styles.listHeaderLabel());
m_splitter1.setPosition(ui::display_w()*3/4 * 4/10);
m_splitter2.setPosition(ui::display_w()*3/4 * 2/10);
gfx::Size displaySize = display()->size();
m_splitter1.setPosition(displaySize.w*3/4 * 4/10);
m_splitter2.setPosition(displaySize.w*3/4 * 2/10);
addChild(&m_splitter1);
m_splitter1.addChild(&m_actionLabel);
@ -881,7 +882,8 @@ void KeyboardShortcutsCommand::onExecute(Context* context)
std::string neededSearchCopy = m_search;
KeyboardShortcutsWindow window(keys, menuKeys, neededSearchCopy);
window.setBounds(gfx::Rect(0, 0, ui::display_w()*3/4, ui::display_h()*3/4));
gfx::Size displaySize = ui::get_desktop_size();
window.setBounds(gfx::Rect(0, 0, displaySize.w*3/4, displaySize.h*3/4));
window.loadLayout();
window.centerWindow();

View File

@ -725,7 +725,7 @@ public:
m_pref.tablet.api(tabletStr);
m_pref.experimental.loadWintabDriver(wintabState);
manager()->nativeWindow()
manager()->display()->nativeWindow()
->setInterpretOneFingerGestureAsMouseMovement(
oneFingerAsMouseMovement()->isSelected());
@ -845,10 +845,8 @@ private:
void updateScreenScaling() {
ui::Manager* manager = ui::Manager::getDefault();
os::Window* window = manager->nativeWindow();
os::instance()->setGpuAcceleration(m_pref.general.gpuAcceleration());
window->setScale(m_pref.general.screenScale());
manager->setNativeWindow(window);
manager->updateAllDisplaysWithNewScale(m_pref.general.screenScale());
}
void onApply() {

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -29,7 +29,7 @@
#include "render/dithering.h"
#include "render/ordered_dither.h"
#include "render/quantization.h"
#include "ui/system.h"
#include "ui/manager.h"
#include "paste_text.xml.h"
@ -124,10 +124,11 @@ private:
}
if (!m_fontPopup->isVisible()) {
gfx::Size displaySize = manager()->display()->size();
gfx::Rect bounds = fontFace()->bounds();
m_fontPopup->showPopup(
gfx::Rect(bounds.x, bounds.y+bounds.h,
ui::display_w()/2, ui::display_h()/2));
displaySize.w/2, displaySize.h/2));
}
else {
m_fontPopup->closeWindow(NULL);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -94,7 +94,7 @@ void UndoCommand::onExecute(Context* context)
current_editor->drawSpriteClipped(
gfx::Region(gfx::Rect(0, 0, sprite->width(), sprite->height())));
current_editor->manager()->flipDisplay();
current_editor->display()->flipDisplay();
base::this_thread::sleep_for(0.01);
}
}

View File

@ -69,7 +69,7 @@ void ScreenshotCommand::onExecute(Context* ctx)
app::ResourceFinder rf(false);
rf.includeDesktopDir("");
os::Window* window = ui::Manager::getDefault()->nativeWindow();
os::Window* window = ui::Manager::getDefault()->display()->nativeWindow();
os::Surface* surface = window->surface();
std::string fn;

View File

@ -70,8 +70,13 @@ public:
void centerConsole() {
initTheme();
remapWindow();
setBounds(gfx::Rect(0, 0, ui::display_w()*9/10, ui::display_h()*6/10));
// TODO center to main window or screen workspace
gfx::Size displaySize = manager()->display()->size();
setBounds(gfx::Rect(0, 0, displaySize.w*9/10, displaySize.h*6/10));
centerWindow();
invalidate();
}
@ -116,14 +121,16 @@ Console::Console(Context* ctx)
if (!ui::is_ui_thread())
return;
if (ctx)
if (ctx) {
m_withUI = (ctx->isUIAvailable());
else
}
else {
m_withUI =
(App::instance() &&
App::instance()->isGui() &&
Manager::getDefault() &&
Manager::getDefault()->nativeWindow());
Manager::getDefault()->display()->nativeWindow());
}
if (!m_withUI)
return;

View File

@ -81,8 +81,13 @@ static struct {
//////////////////////////////////////////////////////////////////////
class CustomizedGuiManager : public Manager
, public LayoutIO {
class CustomizedGuiManager : public ui::Manager
, public ui::LayoutIO {
public:
CustomizedGuiManager(const os::WindowRef& nativeWindow)
: ui::Manager(nativeWindow) {
}
protected:
bool onProcessMessage(Message* msg) override;
#if ENABLE_DEVMODE
@ -90,7 +95,7 @@ protected:
#endif
void onInitTheme(InitThemeEvent& ev) override;
LayoutIO* onGetLayoutIO() override { return this; }
void onNewDisplayConfiguration() override;
void onNewDisplayConfiguration(Display* display) override;
// LayoutIO implementation
std::string loadLayout(Widget* widget) override;
@ -193,8 +198,7 @@ int init_module_gui()
}
// Create the default-manager
manager = new CustomizedGuiManager();
manager->setNativeWindow(main_window.get());
manager = new CustomizedGuiManager(main_window);
// Setup the GUI theme for all widgets
gui_theme = new SkinTheme;
@ -205,17 +209,20 @@ int init_module_gui()
// Handle live resize too redraw the entire manager, dispatch the UI
// messages, and flip the window.
main_window->handleResize =
os::instance()->handleWindowResize =
[](os::Window* window) {
manager->invalidate();
Display* display = manager->getDisplayFromNativeWindow(window);
if (!display)
display = manager->display();
ASSERT(display);
Message* msg = new Message(kResizeDisplayMessage);
msg->setDisplay(display);
msg->setRecipient(manager);
msg->setPropagateToChildren(true);
manager->enqueueMessage(msg);
manager->enqueueMessage(msg);
manager->dispatchMessages();
manager->flipDisplay();
};
// Set graphics options for next time
@ -292,7 +299,7 @@ static void load_gui_config(int& w, int& h, bool& maximized,
static void save_gui_config()
{
os::Window* window = manager->nativeWindow();
os::Window* window = manager->display()->nativeWindow();
if (window) {
set_config_bool("GfxMode", "Maximized", window->isMaximized());
set_config_int("GfxMode", "Width", window->originalWidth());
@ -327,6 +334,8 @@ void update_screen_for_document(const Doc* document)
void load_window_pos(Widget* window, const char* section,
const bool limitMinSize)
{
Size desktopSize = ui::get_desktop_size();
// Default position
Rect orig_pos = window->bounds();
Rect pos = orig_pos;
@ -335,16 +344,16 @@ void load_window_pos(Widget* window, const char* section,
pos = get_config_rect(section, "WindowPos", pos);
if (limitMinSize) {
pos.w = base::clamp(pos.w, orig_pos.w, ui::display_w());
pos.h = base::clamp(pos.h, orig_pos.h, ui::display_h());
pos.w = base::clamp(pos.w, orig_pos.w, desktopSize.w);
pos.h = base::clamp(pos.h, orig_pos.h, desktopSize.h);
}
else {
pos.w = std::min(pos.w, ui::display_w());
pos.h = std::min(pos.h, ui::display_h());
pos.w = std::min(pos.w, desktopSize.w);
pos.h = std::min(pos.h, desktopSize.h);
}
pos.setOrigin(Point(base::clamp(pos.x, 0, ui::display_w()-pos.w),
base::clamp(pos.y, 0, ui::display_h()-pos.h)));
pos.setOrigin(Point(base::clamp(pos.x, 0, desktopSize.w-pos.w),
base::clamp(pos.y, 0, desktopSize.h-pos.h)));
window->setBounds(pos);
}
@ -653,9 +662,9 @@ void CustomizedGuiManager::onInitTheme(InitThemeEvent& ev)
AppMenus::instance()->initTheme();
}
void CustomizedGuiManager::onNewDisplayConfiguration()
void CustomizedGuiManager::onNewDisplayConfiguration(Display* display)
{
Manager::onNewDisplayConfiguration();
Manager::onNewDisplayConfiguration(display);
save_gui_config();
// TODO Should we provide a more generic way for all ui::Window to

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -1884,12 +1884,13 @@ void ColorBar::showPalettePresets()
}
if (!m_palettePopup->isVisible()) {
gfx::Size displaySize = ui::get_desktop_size();
gfx::Rect bounds = m_buttons.getItem(
static_cast<int>(PalButton::PRESETS))->bounds();
m_palettePopup->showPopup(
gfx::Rect(bounds.x, bounds.y+bounds.h,
ui::display_w()/2, ui::display_h()*3/4));
displaySize.w/2, displaySize.h*3/4));
}
else {
m_palettePopup->closeWindow(NULL);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -302,12 +302,13 @@ void ColorButton::openPopup(const bool forcePinned)
m_window->setColor(m_color, ColorPopup::ChangeType);
m_window->openWindow();
gfx::Size displaySize = ui::get_desktop_size();
gfx::Rect winBounds;
if (!pinned || (forcePinned && m_hiddenPopupBounds.isEmpty())) {
winBounds = gfx::Rect(m_window->bounds().origin(),
m_window->sizeHint());
winBounds.x = base::clamp(bounds().x, 0, ui::display_w()-winBounds.w);
if (bounds().y2() <= ui::display_h()-winBounds.h)
winBounds.x = base::clamp(bounds().x, 0, displaySize.w-winBounds.w);
if (bounds().y2() <= displaySize.h-winBounds.h)
winBounds.y = std::max(0, bounds().y2());
else
winBounds.y = std::max(0, bounds().y-winBounds.h);
@ -318,8 +319,8 @@ void ColorButton::openPopup(const bool forcePinned)
else {
winBounds = m_windowDefaultBounds;
}
winBounds.x = base::clamp(winBounds.x, 0, ui::display_w()-winBounds.w);
winBounds.y = base::clamp(winBounds.y, 0, ui::display_h()-winBounds.h);
winBounds.x = base::clamp(winBounds.x, 0, displaySize.w-winBounds.w);
winBounds.y = base::clamp(winBounds.y, 0, displaySize.h-winBounds.h);
m_window->setBounds(winBounds);
m_window->manager()->dispatchMessages();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -147,14 +147,16 @@ private:
paint.style(os::Paint::Fill);
surface->drawRect(gfx::Rect(0, 0, surface->width(), surface->height()), paint);
}
ui::Display* display = this->Base::display();
{
ui::Graphics g(surface, 0, 0);
ui::Graphics g(display, surface, 0, 0);
g.setFont(AddRef(this->font()));
drawFloatingOverlay(g);
}
m_floatingOverlay = base::make_ref<ui::Overlay>(
surface, gfx::Point(),
display, surface, gfx::Point(),
(ui::Overlay::ZOrder)(ui::Overlay::MouseZOrder-1));
ui::OverlayManager::instance()->addOverlay(m_floatingOverlay);
}

View File

@ -349,7 +349,7 @@ void BrushPreview::show(const gfx::Point& screenPos)
// Save area and draw the cursor
if (!(m_type & NATIVE_CROSSHAIR) ||
(m_type & BRUSH_BOUNDARIES)) {
ui::ScreenGraphics g;
ui::ScreenGraphics g(m_editor->display());
ui::SetClip clip(&g);
gfx::Color uiCursorColor = color_utils::color_for_ui(appCursorColor);
forEachBrushPixel(&g, m_screenPosition, spritePos, uiCursorColor, &BrushPreview::savePixelDelegate);
@ -387,7 +387,7 @@ void BrushPreview::hide()
if (m_withModifiedPixels) {
// Restore pixels
ui::ScreenGraphics g;
ui::ScreenGraphics g(m_editor->display());
ui::SetClip clip(&g);
forEachBrushPixel(&g, m_screenPosition, m_editorPosition, gfx::ColorNone,
&BrushPreview::clearPixelDelegate);

View File

@ -947,7 +947,7 @@ void Editor::drawSpriteClipped(const gfx::Region& updateRegion)
Region screenRegion;
getDrawableRegion(screenRegion, kCutTopWindows);
ScreenGraphics screenGraphics;
ScreenGraphics screenGraphics(display());
GraphicsPtr editorGraphics = getGraphics(clientBounds());
for (const Rect& updateRect : updateRegion) {
@ -1163,7 +1163,6 @@ void Editor::drawTileNumbers(ui::Graphics* g, const Cel* cel)
int ti_offset =
static_cast<LayerTilemap*>(cel->layer())->tileset()->baseIndex() - 1;
const gfx::Rect rc = cel->bounds();
const doc::Image* image = cel->image();
std::string text;
for (int y=0; y<image->height(); ++y) {

View File

@ -1029,7 +1029,7 @@ void StandbyState::Decorator::postRenderDecorator(EditorPostRender* render)
if (StandbyState::Decorator::getSymmetryHandles(editor, handles)) {
skin::SkinTheme* theme = static_cast<skin::SkinTheme*>(ui::get_theme());
os::Surface* part = theme->parts.transformationHandle()->bitmap(0);
ScreenGraphics g;
ScreenGraphics g(editor->display());
for (const auto& handle : handles)
g.drawRgbaSurface(part, handle.bounds.x, handle.bounds.y);
}

View File

@ -67,7 +67,7 @@ bool ZoomingState::onMouseUp(Editor* editor, MouseMessage* msg)
bool ZoomingState::onMouseMove(Editor* editor, MouseMessage* msg)
{
gfx::Point pt = (msg->position() - m_startPos);
int threshold = 8 * guiscale() * editor->manager()->nativeWindow()->scale();
int threshold = 8 * guiscale() * editor->display()->nativeWindow()->scale();
if (m_moved || std::sqrt(pt.x*pt.x + pt.y*pt.y) > threshold) {
m_moved = true;

View File

@ -421,7 +421,8 @@ bool FileSelector::show(
FILESEL_TRACE("FILESEL: Start folder '%s' (%p)\n", start_folder_path.c_str(), start_folder);
setMinSize(gfx::Size(ui::display_w()*9/10, ui::display_h()*9/10));
gfx::Size displaySize = ui::get_desktop_size();
setMinSize(gfx::Size(displaySize.w*9/10, displaySize.h*9/10));
remapWindow();
centerWindow();

View File

@ -77,10 +77,8 @@ public:
ui::set_theme(ui::get_theme(), newUIScale);
Manager* manager = Manager::getDefault();
os::Window* window = manager->nativeWindow();
window->setScale(newScreenScale);
manager->setNativeWindow(window);
Manager::getDefault()
->updateAllDisplaysWithNewScale(newScreenScale);
}
};
@ -370,12 +368,13 @@ void MainWindow::onResize(ui::ResizeEvent& ev)
{
app::gen::MainWindow::onResize(ev);
os::Window* window = manager()->nativeWindow();
if ((window) &&
(window->scale()*ui::guiscale() > 2) &&
gfx::Size desktopSize = ui::get_desktop_size();
ui::Display* display = this->display();
if ((display) &&
(display->nativeWindow()->scale()*ui::guiscale() > 2) &&
(!m_scalePanic) &&
(ui::display_w()/ui::guiscale() < 320 ||
ui::display_h()/ui::guiscale() < 260)) {
(desktopSize.w/ui::guiscale() < 320 ||
desktopSize.h/ui::guiscale() < 260)) {
showNotification(m_scalePanic = new ScreenScalePanic);
}
}

View File

@ -224,13 +224,14 @@ bool PreviewEditorWindow::onProcessMessage(ui::Message* msg)
SkinTheme* theme = SkinTheme::instance();
// Default bounds
int width = ui::display_w()/4;
int height = ui::display_h()/4;
gfx::Size desktopSize = ui::get_desktop_size();
const int width = desktopSize.w/4;
const int height = desktopSize.h/4;
int extra = 2*theme->dimensions.miniScrollbarSize();
setBounds(
gfx::Rect(
ui::display_w() - width - ToolBar::instance()->bounds().w - extra,
ui::display_h() - height - StatusBar::instance()->bounds().h - extra,
desktopSize.w - width - ToolBar::instance()->bounds().w - extra,
desktopSize.h - height - StatusBar::instance()->bounds().h - extra,
width, height));
load_window_pos(this, "MiniEditor", false);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -941,6 +941,7 @@ void Tabs::createFloatingOverlay(Tab* tab)
{
ASSERT(!m_floatingOverlay);
ui::Display* display = this->display();
os::SurfaceRef surface = os::instance()->makeRgbaSurface(
tab->width, m_tabsHeight);
@ -953,13 +954,13 @@ void Tabs::createFloatingOverlay(Tab* tab)
surface->drawRect(gfx::Rect(0, 0, surface->width(), surface->height()), paint);
}
{
Graphics g(surface, 0, 0);
Graphics g(display, surface, 0, 0);
g.setFont(AddRef(font()));
drawTab(&g, g.getClipBounds(), tab, 0, true, true);
}
m_floatingOverlay = base::make_ref<ui::Overlay>(
surface, gfx::Point(),
display, surface, gfx::Point(),
(ui::Overlay::ZOrder)(Overlay::MouseZOrder-1));
OverlayManager::instance()->addOverlay(m_floatingOverlay);
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -1242,7 +1242,7 @@ bool Timeline::onProcessMessage(Message* msg)
if (!m_confPopup->isVisible()) {
gfx::Rect bounds = m_confPopup->bounds();
ui::fit_bounds(BOTTOM, gearBounds, bounds);
ui::fit_bounds(display(), BOTTOM, gearBounds, bounds);
m_confPopup->moveWindow(bounds);
m_confPopup->openWindow();

View File

@ -530,7 +530,8 @@ void ToolBar::openTipWindow(int group_index, Tool* tool)
if (tool && m_popupWindow && m_popupWindow->isVisible())
toolrc.x += arrow.x - m_popupWindow->bounds().w;
m_tipWindow->pointAt(TOP | RIGHT, toolrc);
m_tipWindow->pointAt(TOP | RIGHT, toolrc,
ui::Manager::getDefault()->display());
if (m_tipOpened)
m_tipWindow->openWindow();

View File

@ -40,7 +40,7 @@ int main(int argc, char* argv[])
// Do not create a os::System, as we don't need it for testing purposes.
//os::ScopedHandle<os::System> system(os::create_system());
ui::UISystem uiSystem;
ui::Manager uiManager;
ui::Manager uiManager(nullptr);
#endif
exitcode = RUN_ALL_TESTS();

View File

@ -1,4 +1,5 @@
# Aseprite UI Library
# Copyright (C) 2019 Igara Studio S.A.
# Copyright (C) 2001-2018 David Capello
if(WIN32)
@ -13,6 +14,7 @@ add_library(ui-lib
combobox.cpp
component.cpp
cursor.cpp
display.cpp
entry.cpp
event.cpp
fit_bounds.cpp

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -614,8 +614,12 @@ void ComboBox::openListBox()
m_window = new Window(Window::WithoutTitleBar);
View* view = new View();
m_listbox = new ComboBoxListBox(this);
// TODO create a real native window for comboboxes
m_window->setDisplay(display());
m_window->setOnTop(true);
m_window->setWantFocus(false);
m_window->setSizeable(false);
m_window->setMoveable(false);
Widget* viewport = view->viewport();
{
@ -627,8 +631,10 @@ void ComboBox::openListBox()
if (!item->hasFlags(HIDDEN))
size.h += item->sizeHint().h;
int max = std::max(entryBounds.y, ui::display_h() - entryBounds.y2()) - 8*guiscale();
size.h = base::clamp(size.h, textHeight(), max);
const int maxVal =
std::max(entryBounds.y, display()->size().h - entryBounds.y2())
- 8*guiscale();
size.h = base::clamp(size.h, textHeight(), maxVal);
viewport->setMinSize(size);
}
@ -688,7 +694,7 @@ gfx::Rect ComboBox::getListBoxPos() const
gfx::Point(m_button->bounds().x2(),
entryBounds.y2() + m_window->bounds().h));
if (rc.y2() > ui::display_h())
if (rc.y2() > display()->size().h)
rc.offset(0, -(rc.h + entryBounds.h));
return rc;

114
src/ui/display.cpp Normal file
View File

@ -0,0 +1,114 @@
// Aseprite UI Library
// Copyright (C) 2019-2021 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ui/display.h"
#include "base/debug.h"
#include "ui/widget.h"
#include "ui/window.h"
#include <algorithm>
namespace ui {
Display::Display(const os::WindowRef& nativeWindow,
Widget* containedWidget)
: m_nativeWindow(nativeWindow)
, m_containedWidget(containedWidget)
{
ASSERT(m_nativeWindow);
ASSERT(m_containedWidget);
ASSERT(m_containedWidget->type() == kManagerWidget ||
m_containedWidget->type() == kWindowWidget);
m_dirtyRegion = bounds();
}
os::Surface* Display::surface() const
{
return m_nativeWindow->surface();
}
gfx::Size Display::size() const
{
ASSERT(m_nativeWindow);
const int scale = m_nativeWindow->scale();
ASSERT(scale > 0);
return gfx::Size(m_nativeWindow->width() / scale,
m_nativeWindow->height() / scale);
}
void Display::dirtyRect(const gfx::Rect& bounds)
{
m_dirtyRegion.createUnion(m_dirtyRegion, gfx::Region(bounds));
}
void Display::flipDisplay()
{
if (!m_dirtyRegion.isEmpty()) {
// Limit the region to the bounds of the window
m_dirtyRegion &= gfx::Region(bounds());
if (!m_dirtyRegion.isEmpty()) {
// Invalidate the dirty region in the os::Window
m_nativeWindow->invalidateRegion(m_dirtyRegion);
m_dirtyRegion.clear();
}
}
}
void Display::invalidateRect(const gfx::Rect& rect)
{
m_containedWidget->invalidateRect(rect);
}
void Display::invalidateRegion(const gfx::Region& region)
{
m_containedWidget->invalidateRegion(region);
}
void Display::addWindow(Window* window)
{
m_windows.insert(m_windows.begin(), window);
}
void Display::removeWindow(Window* window)
{
auto it = std::find(m_windows.begin(), m_windows.end(), window);
ASSERT(it != m_windows.end())
if (it == m_windows.end())
return;
m_windows.erase(it);
}
// TODO code similar to Manager::handleWindowZOrder()
void Display::handleWindowZOrder(Window* window)
{
removeWindow(window);
if (window->isOnTop())
m_windows.insert(m_windows.begin(), window);
else {
int pos = (int)m_windows.size();
for (auto it=m_windows.rbegin(),
end=m_windows.rend();
it != end; ++it) {
if (static_cast<Window*>(*it)->isOnTop())
break;
--pos;
}
m_windows.insert(m_windows.begin()+pos, window);
}
}
} // namespace ui

83
src/ui/display.h Normal file
View File

@ -0,0 +1,83 @@
// Aseprite UI Library
// Copyright (C) 2019-2021 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef UI_DISPLAY_H_INCLUDED
#define UI_DISPLAY_H_INCLUDED
#pragma once
#include "gfx/rect.h"
#include "gfx/region.h"
#include "gfx/size.h"
#include "os/window.h"
#include <vector>
namespace ui {
class Widget;
class Window;
// Wraps a native window (os::Window). On each "display" we can show
// the main manager or a window (containedWidget), and a set of
// children window that doesn't have a native window counterpart
// (tooltips?)
class Display {
public:
Display(const os::WindowRef& nativeWindow,
Widget* containedWidget);
os::Window* nativeWindow() { return m_nativeWindow.get(); }
os::Surface* surface() const;
gfx::Size size() const;
gfx::Rect bounds() const { return gfx::Rect(size()); }
Widget* containedWidget() const { return m_containedWidget; }
// Mark the given rectangle as a area to be flipped to the real
// screen.
void dirtyRect(const gfx::Rect& bounds);
// Refreshes the real display with the UI content.
void flipDisplay();
// Returns the invalid region in the screen to being updated with
// PaintMessages. This region is cleared when each widget receives
// a paint message.
const gfx::Region& getInvalidRegion() const {
return m_invalidRegion;
}
void addInvalidRegion(const gfx::Region& b) {
m_invalidRegion |= b;
}
void subtractInvalidRegion(const gfx::Region& b) {
m_invalidRegion -= b;
}
void setInvalidRegion(const gfx::Region& b) {
m_invalidRegion = b;
}
void invalidateRect(const gfx::Rect& rect);
void invalidateRegion(const gfx::Region& region);
void addWindow(Window* window);
void removeWindow(Window* window);
void handleWindowZOrder(Window* window);
const std::vector<Window*>& getWindows() const { return m_windows; }
private:
os::WindowRef m_nativeWindow;
Widget* m_containedWidget; // A ui::Manager or a ui::Window
std::vector<Window*> m_windows; // Sub-windows in this display
gfx::Region m_invalidRegion; // Invalid region (we didn't receive paint messages yet for this).
gfx::Region m_dirtyRegion; // Region to flip to the os::Display
};
} // namespace ui
#endif

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -16,7 +16,6 @@
#include "os/draw_text.h"
#include "os/font.h"
#include "os/system.h"
#include "ui/manager.h"
#include "ui/menu.h"
#include "ui/message.h"
#include "ui/scale.h"
@ -452,7 +451,7 @@ gfx::Size Entry::sizeHintWithText(Entry* entry,
+ 2*entry->theme()->getEntryCaretSize(entry).w
+ entry->border().width();
w = std::min(w, ui::display_w()/2);
w = std::min(w, ui::get_desktop_size().w/2);
int h =
+ entry->font()->height()
@ -471,7 +470,7 @@ void Entry::onSizeHint(SizeHintEvent& ev)
+ trailing
+ border().width();
w = std::min(w, ui::display_w()/2);
w = std::min(w, ui::get_desktop_size().w/2);
int h =
+ font()->height()

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
@ -9,14 +9,17 @@
#include "config.h"
#endif
#include "ui/fit_bounds.h"
#include "base/clamp.h"
#include "gfx/rect.h"
#include "ui/base.h"
#include "ui/display.h"
#include "ui/system.h"
namespace ui {
int fit_bounds(int arrowAlign, const gfx::Rect& target, gfx::Rect& bounds)
int fit_bounds(Display* display, int arrowAlign, const gfx::Rect& target, gfx::Rect& bounds)
{
bounds.x = target.x;
bounds.y = target.y;
@ -58,8 +61,9 @@ int fit_bounds(int arrowAlign, const gfx::Rect& target, gfx::Rect& bounds)
break;
}
bounds.x = base::clamp(bounds.x, 0, ui::display_w()-bounds.w);
bounds.y = base::clamp(bounds.y, 0, ui::display_h()-bounds.h);
gfx::Size displaySize = display->size();
bounds.x = base::clamp(bounds.x, 0, displaySize.w-bounds.w);
bounds.y = base::clamp(bounds.y, 0, displaySize.h-bounds.h);
if (target.intersects(bounds)) {
switch (trycount) {

View File

@ -1,18 +1,21 @@
// Aseprite UI Library
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef UI_POINT_AT_H_INCLUDED
#define UI_POINT_AT_H_INCLUDED
#ifndef UI_FIT_BOUNDS_H_INCLUDED
#define UI_FIT_BOUNDS_H_INCLUDED
#pragma once
#include "gfx/fwd.h"
namespace ui {
int fit_bounds(int arrowAlign, const gfx::Rect& target, gfx::Rect& bounds);
class Display;
int fit_bounds(Display* display, int arrowAlign, const gfx::Rect& target, gfx::Rect& bounds);
} // namespace ui

View File

@ -24,7 +24,7 @@
#include "os/surface.h"
#include "os/system.h"
#include "os/window.h"
#include "ui/manager.h"
#include "ui/display.h"
#include "ui/scale.h"
#include "ui/theme.h"
@ -34,8 +34,9 @@
namespace ui {
Graphics::Graphics(const os::SurfaceRef& surface, int dx, int dy)
: m_surface(surface)
Graphics::Graphics(Display* display, const os::SurfaceRef& surface, int dx, int dy)
: m_display(display)
, m_surface(surface)
, m_dx(dx)
, m_dy(dy)
{
@ -45,8 +46,8 @@ Graphics::~Graphics()
{
// If we were drawing in the screen surface, we mark these regions
// as dirty for the final flip.
if (m_surface == os::instance()->defaultWindow()->surface())
Manager::getDefault()->dirtyRect(m_dirtyBounds);
if (m_display)
m_display->dirtyRect(m_dirtyBounds);
}
int Graphics::width() const
@ -611,8 +612,8 @@ void Graphics::dirty(const gfx::Rect& bounds)
//////////////////////////////////////////////////////////////////////
// ScreenGraphics
ScreenGraphics::ScreenGraphics()
: Graphics(AddRef(os::instance()->defaultWindow()->surface()), 0, 0)
ScreenGraphics::ScreenGraphics(Display* display)
: Graphics(display, AddRef(display->surface()), 0, 0)
{
setFont(AddRef(get_theme()->getDefaultFont()));
}

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -33,7 +33,9 @@ namespace os {
}
namespace ui {
using os::Paint;
using os::Paint; // Define ui::Paint = os::Paint
class Display;
// Class to render a widget in the screen.
class Graphics {
@ -44,7 +46,7 @@ namespace ui {
Checked,
};
Graphics(const os::SurfaceRef& surface, int dx, int dy);
Graphics(Display* display, const os::SurfaceRef& surface, int dx, int dy);
~Graphics();
int width() const;
@ -127,6 +129,7 @@ namespace ui {
gfx::Size doUIStringAlgorithm(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Rect& rc, int align, bool draw);
void dirty(const gfx::Rect& bounds);
Display* m_display;
os::SurfaceRef m_surface;
int m_dx;
int m_dy;
@ -138,7 +141,7 @@ namespace ui {
// Class to draw directly in the screen.
class ScreenGraphics : public Graphics {
public:
ScreenGraphics();
ScreenGraphics(Display* display);
virtual ~ScreenGraphics();
};

View File

@ -183,10 +183,11 @@ void IntEntry::openPopup()
Rect rc = bounds();
gfx::Size sz = m_popupWindow->sizeHint();
gfx::Size displaySize = display()->size();
rc.w = 128*guiscale();
if (rc.x+rc.w > ui::display_w())
if (rc.x+rc.w > displaySize.w)
rc.x = rc.x-rc.w+bounds().w;
if (rc.y+rc.h+sz.h < ui::display_h())
if (rc.y+rc.h+sz.h < displaySize.h)
rc.y += rc.h;
else
rc.y -= sz.h;

View File

@ -76,7 +76,6 @@ typedef std::list<Message*> Messages;
typedef std::list<Filter*> Filters;
Manager* Manager::m_defaultManager = nullptr;
gfx::Region Manager::m_dirtyRegion;
#ifdef DEBUG_UI_THREADS
static base::thread::native_id_type manager_thread = 0;
@ -160,13 +159,15 @@ bool Manager::widgetAssociatedToManager(Widget* widget)
widget) != mouse_widgets_list.end());
}
Manager::Manager()
Manager::Manager(const os::WindowRef& nativeWindow)
: Widget(kManagerWidget)
, m_window(nullptr)
, m_eventQueue(nullptr)
, m_display(nativeWindow, this)
, m_eventQueue(os::instance()->eventQueue())
, m_lockedWindow(nullptr)
, m_mouseButton(kButtonNone)
{
nativeWindow->setUserData(&m_display);
#ifdef DEBUG_UI_THREADS
ASSERT(!manager_thread);
manager_thread = base::this_thread::native_id();
@ -183,14 +184,15 @@ Manager::Manager()
capture_widget = nullptr;
}
setBounds(gfx::Rect(0, 0, ui::display_w(), ui::display_h()));
setBounds(m_display.bounds());
setVisible(true);
m_dirtyRegion = bounds();
// Default manager is the first one (and is always visible).
if (!m_defaultManager)
m_defaultManager = this;
// TODO check if this is needed
onNewDisplayConfiguration(&m_display);
}
Manager::~Manager()
@ -226,16 +228,12 @@ Manager::~Manager()
}
}
void Manager::setNativeWindow(os::Window* window)
Display* Manager::getDisplayFromNativeWindow(os::Window* window) const
{
base::ScopedValue<bool> lock(
auto_window_adjustment, false,
auto_window_adjustment);
m_window = window;
m_eventQueue = os::instance()->eventQueue();
onNewDisplayConfiguration();
if (window)
return window->userData<Display>();
else
return nullptr;
}
void Manager::run()
@ -253,11 +251,8 @@ void Manager::run()
loop.pumpMessages();
}
void Manager::flipDisplay()
void Manager::flipAllDisplays()
{
if (!m_window)
return;
OverlayManager* overlays = OverlayManager::instance();
update_cursor_overlay();
@ -265,15 +260,15 @@ void Manager::flipDisplay()
// Draw overlays.
overlays->drawOverlays();
// Invalidate the dirty region in the laf::os::Display (the real OS window).
m_dirtyRegion.createIntersection(
m_dirtyRegion,
gfx::Region(gfx::Rect(0, 0, ui::display_w(), ui::display_h())));
m_display.flipDisplay();
}
if (!m_dirtyRegion.isEmpty()) {
m_window->invalidateRegion(m_dirtyRegion);
m_dirtyRegion.clear();
}
void Manager::updateAllDisplaysWithNewScale(int scale)
{
os::Window* nativeWindow = m_display.nativeWindow();
nativeWindow->setScale(scale);
onNewDisplayConfiguration(&m_display);
}
bool Manager::generateMessages()
@ -308,7 +303,8 @@ bool Manager::generateMessages()
return false;
}
void Manager::generateSetCursorMessage(const gfx::Point& mousePos,
void Manager::generateSetCursorMessage(Display* display,
const gfx::Point& mousePos,
KeyModifiers modifiers,
PointerType pointerType)
{
@ -319,7 +315,8 @@ void Manager::generateSetCursorMessage(const gfx::Point& mousePos,
if (dst)
enqueueMessage(
newMouseMessage(
kSetCursorMessage, dst,
kSetCursorMessage,
display, dst,
mousePos,
pointerType,
m_mouseButton,
@ -375,10 +372,15 @@ void Manager::generateMessagesFromOSEvents()
if (osEvent.type() == os::Event::None)
break;
Display* display = getDisplayFromNativeWindow(osEvent.window().get());
if (!display)
display = this->display();
switch (osEvent.type()) {
case os::Event::CloseApp: {
Message* msg = new Message(kCloseDisplayMessage);
msg->setDisplay(display);
msg->setRecipient(this);
msg->setPropagateToChildren(true);
enqueueMessage(msg);
@ -387,6 +389,7 @@ void Manager::generateMessagesFromOSEvents()
case os::Event::CloseWindow: {
Message* msg = new Message(kCloseDisplayMessage);
msg->setDisplay(display);
msg->setRecipient(this);
msg->setPropagateToChildren(true);
enqueueMessage(msg);
@ -395,6 +398,7 @@ void Manager::generateMessagesFromOSEvents()
case os::Event::ResizeWindow: {
Message* msg = new Message(kResizeDisplayMessage);
msg->setDisplay(display);
msg->setRecipient(this);
msg->setPropagateToChildren(true);
enqueueMessage(msg);
@ -403,6 +407,7 @@ void Manager::generateMessagesFromOSEvents()
case os::Event::DropFiles: {
Message* msg = new DropFilesMessage(osEvent.files());
msg->setDisplay(display);
msg->setRecipient(this);
enqueueMessage(msg);
break;
@ -419,6 +424,8 @@ void Manager::generateMessagesFromOSEvents()
osEvent.unicodeChar(),
osEvent.repeat());
msg->setDisplay(display);
if (osEvent.isDeadKey())
static_cast<KeyMessage*>(msg)->setDeadKey(true);
@ -449,6 +456,7 @@ void Manager::generateMessagesFromOSEvents()
case os::Event::MouseMove: {
_internal_set_mouse_position(osEvent.position());
handleMouseMove(
display,
osEvent.position(),
osEvent.modifiers(),
osEvent.pointerType(),
@ -459,6 +467,7 @@ void Manager::generateMessagesFromOSEvents()
case os::Event::MouseDown: {
handleMouseDown(
display,
osEvent.position(),
m_mouseButton = mouse_button_from_os_to_ui(osEvent),
osEvent.modifiers(),
@ -468,6 +477,7 @@ void Manager::generateMessagesFromOSEvents()
case os::Event::MouseUp: {
handleMouseUp(
display,
osEvent.position(),
mouse_button_from_os_to_ui(osEvent),
osEvent.modifiers(),
@ -478,6 +488,7 @@ void Manager::generateMessagesFromOSEvents()
case os::Event::MouseDoubleClick: {
handleMouseDoubleClick(
display,
osEvent.position(),
m_mouseButton = mouse_button_from_os_to_ui(osEvent),
osEvent.modifiers(),
@ -486,7 +497,8 @@ void Manager::generateMessagesFromOSEvents()
}
case os::Event::MouseWheel: {
handleMouseWheel(osEvent.position(),
handleMouseWheel(display,
osEvent.position(),
osEvent.modifiers(),
osEvent.pointerType(),
osEvent.wheelDelta(),
@ -497,7 +509,8 @@ void Manager::generateMessagesFromOSEvents()
case os::Event::TouchMagnify: {
_internal_set_mouse_position(osEvent.position());
handleTouchMagnify(osEvent.position(),
handleTouchMagnify(display,
osEvent.position(),
osEvent.modifiers(),
osEvent.magnification());
break;
@ -515,13 +528,20 @@ void Manager::generateMessagesFromOSEvents()
// Generate just one kSetCursorMessage for the last mouse position
if (lastMouseMoveEvent.type() != os::Event::None) {
osEvent = lastMouseMoveEvent;
generateSetCursorMessage(osEvent.position(),
Display* display = getDisplayFromNativeWindow(osEvent.window().get());
if (!display)
display = this->display();
generateSetCursorMessage(display,
osEvent.position(),
osEvent.modifiers(),
osEvent.pointerType());
}
}
void Manager::handleMouseMove(const gfx::Point& mousePos,
void Manager::handleMouseMove(Display* display,
const gfx::Point& mousePos,
const KeyModifiers modifiers,
const PointerType pointerType,
const float pressure)
@ -532,7 +552,8 @@ void Manager::handleMouseMove(const gfx::Point& mousePos,
Widget* dst = (capture_widget ? capture_widget: mouse_widget);
enqueueMessage(
newMouseMessage(
kMouseMoveMessage, dst,
kMouseMoveMessage,
display, dst,
mousePos,
pointerType,
m_mouseButton,
@ -542,7 +563,8 @@ void Manager::handleMouseMove(const gfx::Point& mousePos,
pressure));
}
void Manager::handleMouseDown(const gfx::Point& mousePos,
void Manager::handleMouseDown(Display* display,
const gfx::Point& mousePos,
MouseButton mouseButton,
KeyModifiers modifiers,
PointerType pointerType)
@ -552,6 +574,7 @@ void Manager::handleMouseDown(const gfx::Point& mousePos,
enqueueMessage(
newMouseMessage(
kMouseDownMessage,
display,
(capture_widget ? capture_widget: mouse_widget),
mousePos,
pointerType,
@ -559,7 +582,8 @@ void Manager::handleMouseDown(const gfx::Point& mousePos,
modifiers));
}
void Manager::handleMouseUp(const gfx::Point& mousePos,
void Manager::handleMouseUp(Display* display,
const gfx::Point& mousePos,
MouseButton mouseButton,
KeyModifiers modifiers,
PointerType pointerType)
@ -567,6 +591,7 @@ void Manager::handleMouseUp(const gfx::Point& mousePos,
enqueueMessage(
newMouseMessage(
kMouseUpMessage,
display,
(capture_widget ? capture_widget: mouse_widget),
mousePos,
pointerType,
@ -574,7 +599,8 @@ void Manager::handleMouseUp(const gfx::Point& mousePos,
modifiers));
}
void Manager::handleMouseDoubleClick(const gfx::Point& mousePos,
void Manager::handleMouseDoubleClick(Display* display,
const gfx::Point& mousePos,
MouseButton mouseButton,
KeyModifiers modifiers,
PointerType pointerType)
@ -584,12 +610,13 @@ void Manager::handleMouseDoubleClick(const gfx::Point& mousePos,
enqueueMessage(
newMouseMessage(
kDoubleClickMessage,
dst, mousePos, pointerType,
display, dst, mousePos, pointerType,
mouseButton, modifiers));
}
}
void Manager::handleMouseWheel(const gfx::Point& mousePos,
void Manager::handleMouseWheel(Display* display,
const gfx::Point& mousePos,
KeyModifiers modifiers,
PointerType pointerType,
const gfx::Point& wheelDelta,
@ -597,12 +624,14 @@ void Manager::handleMouseWheel(const gfx::Point& mousePos,
{
enqueueMessage(newMouseMessage(
kMouseWheelMessage,
display,
(capture_widget ? capture_widget: mouse_widget),
mousePos, pointerType, m_mouseButton, modifiers,
wheelDelta, preciseWheel));
}
void Manager::handleTouchMagnify(const gfx::Point& mousePos,
void Manager::handleTouchMagnify(Display* display,
const gfx::Point& mousePos,
const KeyModifiers modifiers,
const double magnification)
{
@ -614,6 +643,7 @@ void Manager::handleTouchMagnify(const gfx::Point& mousePos,
mousePos,
magnification);
msg->setDisplay(display);
msg->setRecipient(widget);
enqueueMessage(msg);
@ -622,6 +652,8 @@ void Manager::handleTouchMagnify(const gfx::Point& mousePos,
// Handles Z order: Send the window to top (only when you click in a
// window that aren't the desktop).
//
// TODO code similar to Display::handleWindowZOrder()
void Manager::handleWindowZOrder()
{
if (capture_widget || !mouse_widget)
@ -642,6 +674,8 @@ void Manager::handleWindowZOrder()
(window != win_manager->getTopWindow())) {
base::ScopedValue<Widget*> scoped(m_lockedWindow, window, nullptr);
window->display()->handleWindowZOrder(window);
// Put it in the top of the list
win_manager->removeChild(window);
@ -718,8 +752,8 @@ void Manager::dispatchMessages()
flushRedraw();
pumpQueue();
// Flip the back-buffer to the real display.
flipDisplay();
// Flip back-buffers to real displays.
flipAllDisplays();
}
}
}
@ -854,8 +888,11 @@ void Manager::setMouse(Widget* widget)
// Put the mouse
mouse_widget = widget;
if (widget) {
Display* display = mouse_widget->display();
auto msg = newMouseMessage(
kMouseEnterMessage, nullptr,
kMouseEnterMessage,
display, nullptr,
get_mouse_position(),
PointerType::Unknown,
m_mouseButton,
@ -865,7 +902,8 @@ void Manager::setMouse(Widget* widget)
msg->setPropagateToParent(true);
msg->setCommonAncestor(commonAncestor);
enqueueMessage(msg);
generateSetCursorMessage(get_mouse_position(),
generateSetCursorMessage(display,
get_mouse_position(),
kKeyUninitializedModifier,
PointerType::Unknown);
@ -888,7 +926,10 @@ void Manager::setCapture(Widget* widget)
widget->enableFlags(HAS_CAPTURE);
capture_widget = widget;
m_window->captureMouse();
Display* display = (widget ? widget->display(): &m_display);
ASSERT(display && display->nativeWindow());
if (display && display->nativeWindow())
display->nativeWindow()->captureMouse();
}
// Sets the focus to the "magnetic" widget inside the window
@ -925,10 +966,14 @@ void Manager::freeMouse()
void Manager::freeCapture()
{
if (capture_widget) {
Display* display = capture_widget->display();
capture_widget->disableFlags(HAS_CAPTURE);
capture_widget = nullptr;
m_window->releaseMouse();
ASSERT(display && display->nativeWindow());
if (display && display->nativeWindow())
display->nativeWindow()->releaseMouse();
}
}
@ -1073,11 +1118,6 @@ bool Manager::isFocusMovementMessage(Message* msg)
return false;
}
void Manager::dirtyRect(const gfx::Rect& bounds)
{
m_dirtyRegion.createUnion(m_dirtyRegion, gfx::Region(bounds));
}
// Configures the window for begin the loop
void Manager::_openWindow(Window* window)
{
@ -1100,6 +1140,9 @@ void Manager::_openWindow(Window* window)
// Relayout
window->layout();
// Same display as the manager.
window->setDisplay(this->display());
// Dirty the entire window and show it
window->setVisible(true);
window->invalidate();
@ -1121,6 +1164,8 @@ void Manager::_openWindow(Window* window)
void Manager::_closeWindow(Window* window, bool redraw_background)
{
window->setDisplay(nullptr);
if (!hasChild(window))
return;
@ -1189,7 +1234,7 @@ bool Manager::onProcessMessage(Message* msg)
return true;
case kResizeDisplayMessage:
onNewDisplayConfiguration();
onNewDisplayConfiguration(msg->display());
break;
case kKeyDownMessage:
@ -1236,7 +1281,7 @@ void Manager::onResize(ResizeEvent& ev)
setBoundsQuietly(new_pos);
// The whole manager area is invalid now.
m_invalidRegion = gfx::Region(new_pos);
m_display.setInvalidRegion(gfx::Region(new_pos));
const int dx = new_pos.x - old_pos.x;
const int dy = new_pos.y - old_pos.y;
@ -1306,11 +1351,12 @@ void Manager::onInitTheme(InitThemeEvent& ev)
window->layout();
}
else {
gfx::Size displaySize = m_display.size();
gfx::Rect bounds = window->bounds();
bounds *= newUIScale;
bounds /= oldUIScale;
bounds.x = base::clamp(bounds.x, 0, m_window->width() - bounds.w);
bounds.y = base::clamp(bounds.y, 0, m_window->height() - bounds.h);
bounds.x = base::clamp(bounds.x, 0, displaySize.w - bounds.w);
bounds.y = base::clamp(bounds.y, 0, displaySize.h - bounds.h);
window->setBounds(bounds);
}
}
@ -1322,19 +1368,19 @@ LayoutIO* Manager::onGetLayoutIO()
return nullptr;
}
void Manager::onNewDisplayConfiguration()
void Manager::onNewDisplayConfiguration(Display* display)
{
if (m_window) {
int w = m_window->width() / m_window->scale();
int h = m_window->height() / m_window->scale();
if ((bounds().w != w ||
bounds().h != h)) {
setBounds(gfx::Rect(0, 0, w, h));
}
ASSERT(display);
Widget* container = display->containedWidget();
gfx::Size displaySize = display->size();
if ((bounds().w != displaySize.w ||
bounds().h != displaySize.h)) {
container->setBounds(gfx::Rect(displaySize));
}
_internal_set_mouse_window(m_window);
invalidate();
_internal_set_mouse_display(display);
container->invalidate();
flushRedraw();
}
@ -1497,10 +1543,11 @@ bool Manager::sendMessageToWidget(Message* msg, Widget* widget)
PaintMessage* paintMsg = static_cast<PaintMessage*>(msg);
// TODO use paintMsg->display() here
// Restore overlays in the region that we're going to paint.
OverlayManager::instance()->restoreOverlappedAreas(paintMsg->rect());
os::Surface* surface = m_window->surface();
os::Surface* surface = paintMsg->display()->surface();
surface->saveClip();
if (surface->clipRect(paintMsg->rect())) {
@ -1520,8 +1567,7 @@ bool Manager::sendMessageToWidget(Message* msg, Widget* widget)
}
if (m_window) {
m_window->invalidateRegion(
gfx::Region(gfx::Rect(0, 0, display_w(), display_h())));
m_window->invalidateRegion(gfx::Region(m_window->bounds()));
// TODO m_window->update() ??
}
@ -1539,7 +1585,7 @@ bool Manager::sendMessageToWidget(Message* msg, Widget* widget)
// As this kPaintMessage's rectangle was updated, we can
// remove it from "m_invalidRegion".
m_invalidRegion -= gfx::Region(paintMsg->rect());
paintMsg->display()->subtractInvalidRegion(gfx::Region(paintMsg->rect()));
}
else {
// Call the message handler
@ -1690,6 +1736,7 @@ Widget* Manager::findMagneticWidget(Widget* widget)
// static
Message* Manager::newMouseMessage(
MessageType type,
Display* display,
Widget* widget,
const gfx::Point& mousePos,
PointerType pointerType,
@ -1716,6 +1763,9 @@ Message* Manager::newMouseMessage(
type, pointerType, button, modifiers, mousePos,
wheelDelta, preciseWheel, pressure);
if (display)
msg->setDisplay(display);
if (widget)
msg->setRecipient(widget);

View File

@ -10,6 +10,7 @@
#pragma once
#include "gfx/region.h"
#include "ui/display.h"
#include "ui/keys.h"
#include "ui/message_type.h"
#include "ui/mouse_button.h"
@ -32,17 +33,19 @@ namespace ui {
static Manager* getDefault() { return m_defaultManager; }
static bool widgetAssociatedToManager(Widget* widget);
Manager();
Manager(const os::WindowRef& nativeWindow);
~Manager();
os::Window* nativeWindow() { return m_window; }
void setNativeWindow(os::Window* window);
Display* display() { return &m_display; }
Display* getDisplayFromNativeWindow(os::Window* window) const;
// Executes the main message loop.
void run();
// Refreshes the real display with the UI content.
void flipDisplay();
// Refreshes all real displays with the UI content.
void flipAllDisplays();
void updateAllDisplaysWithNewScale(int scale);
// Adds the given "msg" message to the queue of messages to be
// dispached. "msg" cannot be used after this function, it'll be
@ -86,21 +89,6 @@ namespace ui {
bool isFocusMovementMessage(Message* msg);
bool processFocusMovementMessage(Message* msg);
// Returns the invalid region in the screen to being updated with
// PaintMessages. This region is cleared when each widget receives
// a paint message.
const gfx::Region& getInvalidRegion() const {
return m_invalidRegion;
}
void addInvalidRegion(const gfx::Region& b) {
m_invalidRegion |= b;
}
// Mark the given rectangle as a area to be flipped to the real
// screen
void dirtyRect(const gfx::Rect& bounds);
void _openWindow(Window* window);
void _closeWindow(Window* window, bool redraw_background);
@ -112,35 +100,43 @@ namespace ui {
void onBroadcastMouseMessage(WidgetsList& targets) override;
void onInitTheme(InitThemeEvent& ev) override;
virtual LayoutIO* onGetLayoutIO();
virtual void onNewDisplayConfiguration();
virtual void onNewDisplayConfiguration(Display* display);
private:
void generateSetCursorMessage(const gfx::Point& mousePos,
void generateSetCursorMessage(Display* display,
const gfx::Point& mousePos,
KeyModifiers modifiers,
PointerType pointerType);
void generateMessagesFromOSEvents();
void handleMouseMove(const gfx::Point& mousePos,
void handleMouseMove(Display* display,
const gfx::Point& mousePos,
const KeyModifiers modifiers,
const PointerType pointerType,
const float pressure);
void handleMouseDown(const gfx::Point& mousePos,
void handleMouseDown(Display* display,
const gfx::Point& mousePos,
MouseButton mouseButton,
KeyModifiers modifiers,
PointerType pointerType);
void handleMouseUp(const gfx::Point& mousePos,
void handleMouseUp(Display* display,
const gfx::Point& mousePos,
MouseButton mouseButton,
KeyModifiers modifiers,
PointerType pointerType);
void handleMouseDoubleClick(const gfx::Point& mousePos,
void handleMouseDoubleClick(Display* display,
const gfx::Point& mousePos,
MouseButton mouseButton,
KeyModifiers modifiers,
PointerType pointerType);
void handleMouseWheel(const gfx::Point& mousePos,
void handleMouseWheel(Display* display,
const gfx::Point& mousePos,
KeyModifiers modifiers,
PointerType pointerType,
const gfx::Point& wheelDelta,
bool preciseWheel);
void handleTouchMagnify(const gfx::Point& mousePos,
void handleTouchMagnify(Display* display,
const gfx::Point& mousePos,
const KeyModifiers modifiers,
const double magnification);
void handleWindowZOrder();
@ -154,7 +150,9 @@ namespace ui {
static Widget* findMagneticWidget(Widget* widget);
static Message* newMouseMessage(
MessageType type,
Widget* widget, const gfx::Point& mousePos,
Display* display,
Widget* widget,
const gfx::Point& mousePos,
PointerType pointerType,
MouseButton button,
KeyModifiers modifiers,
@ -164,12 +162,10 @@ namespace ui {
void broadcastKeyMsg(Message* msg);
static Manager* m_defaultManager;
static gfx::Region m_dirtyRegion;
WidgetsList m_garbage;
os::Window* m_window;
Display m_display;
os::EventQueue* m_eventQueue;
gfx::Region m_invalidRegion; // Invalid region (we didn't receive paint messages yet for this).
// This member is used to make freeWidget() a no-op when we
// restack a window if the user clicks on it.

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -14,6 +14,7 @@
#include "base/clamp.h"
#include "gfx/size.h"
#include "os/font.h"
#include "ui/display.h"
#include "ui/intern.h"
#include "ui/ui.h"
@ -102,14 +103,15 @@ static MenuItem* find_previtem(Menu* menu, MenuItem* menuitem);
static void add_scrollbars_if_needed(Window* window)
{
const gfx::Rect rc0 = window->bounds();
gfx::Size displaySize = window->display()->size();
gfx::Rect rc = rc0;
if (rc.x < 0) {
rc.w += rc.x;
rc.x = 0;
}
if (rc.x2() > ui::display_w()) {
rc.w = ui::display_w() - rc.x;
if (rc.x2() > displaySize.w) {
rc.w = displaySize.w - rc.x;
}
bool vscrollbarsAdded = false;
@ -118,8 +120,8 @@ static void add_scrollbars_if_needed(Window* window)
rc.y = 0;
vscrollbarsAdded = true;
}
if (rc.y2() > ui::display_h()) {
rc.h = ui::display_h() - rc.y;
if (rc.y2() > displaySize.h) {
rc.h = displaySize.h - rc.y;
vscrollbarsAdded = true;
}
@ -133,11 +135,11 @@ static void add_scrollbars_if_needed(Window* window)
if (vscrollbarsAdded) {
rc.w += 2*view->verticalBar()->getBarWidth();
if (rc.x2() > ui::display_w()) {
rc.x = ui::display_w() - rc.w;
if (rc.x2() > displaySize.w) {
rc.x = displaySize.w - rc.w;
if (rc.x < 0) {
rc.x = 0;
rc.w = ui::display_w();
rc.w = displaySize.w;
}
}
}
@ -311,6 +313,7 @@ void Menu::showPopup(const gfx::Point& pos)
MenuBaseData* base = menubox->createBase();
base->was_clicked = true;
window->setMoveable(false); // Can't move the window
window->setSizeable(false); // Can't resize the window
// Set children
menubox->setMenu(this);
@ -320,9 +323,10 @@ void Menu::showPopup(const gfx::Point& pos)
window->remapWindow();
// Menubox position
gfx::Size displaySize = display()->size();
window->positionWindow(
base::clamp(pos.x, 0, ui::display_w() - window->bounds().w),
base::clamp(pos.y, 0, ui::display_h() - window->bounds().h));
base::clamp(pos.x, 0, displaySize.w - window->bounds().w),
base::clamp(pos.y, 0, displaySize.h - window->bounds().h));
add_scrollbars_if_needed(window.get());
@ -821,9 +825,10 @@ bool MenuItem::onProcessMessage(Message* msg)
// Menubox position
Rect pos = window->bounds();
Size displaySize = display()->size();
if (inBar()) {
pos.x = base::clamp(bounds().x, 0, ui::display_w()-pos.w);
pos.x = base::clamp(bounds().x, 0, displaySize.w-pos.w);
pos.y = std::max(0, bounds().y2());
}
else {
@ -832,9 +837,9 @@ bool MenuItem::onProcessMessage(Message* msg)
int x, y = bounds().y-3*guiscale();
Rect r1(0, 0, pos.w, pos.h), r2(0, 0, pos.w, pos.h);
r1.x = x_left = base::clamp(x_left, 0, ui::display_w()-pos.w);
r2.x = x_right = base::clamp(x_right, 0, ui::display_w()-pos.w);
r1.y = r2.y = y = base::clamp(y, 0, ui::display_h()-pos.h);
r1.x = x_left = base::clamp(x_left, 0, displaySize.w-pos.w);
r2.x = x_right = base::clamp(x_right, 0, displaySize.w-pos.w);
r1.y = r2.y = y = base::clamp(y, 0, displaySize.h-pos.h);
// Calculate both intersections
gfx::Rect s1 = r1.createIntersection(old_pos);
@ -1408,6 +1413,7 @@ MenuBoxWindow::MenuBoxWindow(MenuBox* menubox)
: Window(WithoutTitleBar, "")
{
setMoveable(false); // Can't move the window
setSizeable(false); // Can't resize the window
addChild(menubox);
remapWindow();
}

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -13,7 +13,6 @@
#include "base/memory.h"
#include "os/system.h"
#include "ui/manager.h"
#include "ui/widget.h"
#include <cstring>
@ -23,6 +22,7 @@ namespace ui {
Message::Message(MessageType type, KeyModifiers modifiers)
: m_type(type)
, m_flags(0)
, m_display(nullptr)
, m_recipient(nullptr)
, m_commonAncestor(nullptr)
{
@ -36,6 +36,11 @@ Message::~Message()
{
}
void Message::setDisplay(Display* display)
{
m_display = display;
}
void Message::setRecipient(Widget* widget)
{
ASSERT(m_recipient == nullptr);

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -20,6 +20,7 @@
namespace ui {
class Display;
class Timer;
class Widget;
@ -35,6 +36,7 @@ namespace ui {
virtual ~Message();
MessageType type() const { return m_type; }
Display* display() const { return m_display; }
Widget* recipient() const { return m_recipient; }
bool fromFilter() const { return hasFlag(FromFilter); }
void setFromFilter(const bool state) { setFlag(FromFilter, state); }
@ -50,6 +52,7 @@ namespace ui {
bool onlyCmdPressed() const { return m_modifiers == kKeyCmdModifier; }
bool onlyWinPressed() const { return m_modifiers == kKeyWinModifier; }
void setDisplay(Display* display);
void setRecipient(Widget* widget);
void removeRecipient(Widget* widget);
@ -72,6 +75,7 @@ namespace ui {
MessageType m_type; // Type of message
int m_flags; // Special flags for this message
Display* m_display;
Widget* m_recipient; // Recipient of this message
Widget* m_commonAncestor; // Common ancestor between the Leave <-> Enter messages
KeyModifiers m_modifiers; // Key modifiers pressed when message was created
@ -101,7 +105,9 @@ namespace ui {
class PaintMessage : public Message {
public:
PaintMessage(int count, const gfx::Rect& rect)
: Message(kPaintMessage), m_count(count), m_rect(rect) {
: Message(kPaintMessage)
, m_count(count)
, m_rect(rect) {
}
int count() const { return m_count; }

View File

@ -22,9 +22,13 @@ namespace ui {
using namespace gfx;
void move_region(Manager* manager, const Region& region, int dx, int dy)
void move_region(Display* display, const Region& region, int dx, int dy)
{
os::Window* window = manager->nativeWindow();
ASSERT(display);
if (!display)
return;
os::Window* window = display->nativeWindow();
ASSERT(window);
if (!window)
return;
@ -43,7 +47,7 @@ void move_region(Manager* manager, const Region& region, int dx, int dy)
surface->scrollTo(rc, dx, dy);
rc.offset(dx, dy);
manager->dirtyRect(rc);
display->dirtyRect(rc);
}
// As rectangles in the region internals are separated by bands
// through the y-axis, we can sort the rectangles by y-axis and then
@ -87,7 +91,7 @@ void move_region(Manager* manager, const Region& region, int dx, int dy)
surface->scrollTo(rc, dx, dy);
rc.offset(dx, dy);
manager->dirtyRect(rc);
display->dirtyRect(rc);
}
}

View File

@ -1,4 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
@ -12,9 +13,9 @@
namespace ui {
class Manager;
class Display;
void move_region(Manager* manager, const gfx::Region& region, int dx, int dy);
void move_region(Display* display, const gfx::Region& region, int dx, int dy);
} // namespace ui

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
@ -13,14 +13,16 @@
#include "os/surface.h"
#include "os/system.h"
#include "ui/manager.h"
#include "ui/display.h"
namespace ui {
Overlay::Overlay(const os::SurfaceRef& overlaySurface,
Overlay::Overlay(Display* display,
const os::SurfaceRef& overlaySurface,
const gfx::Point& pos,
ZOrder zorder)
: m_surface(overlaySurface)
: m_display(display)
, m_surface(overlaySurface)
, m_overlap(nullptr)
, m_captured(nullptr)
, m_pos(pos)
@ -33,11 +35,8 @@ Overlay::~Overlay()
ASSERT(!m_captured);
if (m_surface) {
Manager* manager = Manager::getDefault();
if (manager)
manager->invalidateRect(gfx::Rect(m_pos.x, m_pos.y,
m_surface->width(),
m_surface->height()));
if (m_display)
m_display->invalidateRect(bounds());
m_surface.reset();
}
@ -69,7 +68,7 @@ void Overlay::drawOverlay()
os::SurfaceLock lock(m_surface.get());
m_captured->drawRgbaSurface(m_surface.get(), m_pos.x, m_pos.y);
Manager::getDefault()->dirtyRect(
m_display->dirtyRect(
gfx::Rect(m_pos.x, m_pos.y,
m_surface->width(),
m_surface->height()));
@ -83,24 +82,27 @@ void Overlay::moveOverlay(const gfx::Point& newPos)
m_pos = newPos;
}
void Overlay::captureOverlappedArea(os::Surface* screen)
void Overlay::captureOverlappedArea()
{
if (!m_surface ||
m_captured)
return;
os::Surface* displaySurface = m_display->surface();
os::SurfaceLock lockDisplaySurface(displaySurface);
if (!m_overlap) {
// Use the same color space for the overlay as in the screen
m_overlap = os::instance()->makeSurface(m_surface->width(),
m_surface->height(),
screen->colorSpace());
displaySurface->colorSpace());
}
os::SurfaceLock lock(m_overlap.get());
screen->blitTo(m_overlap.get(), m_pos.x, m_pos.y, 0, 0,
m_overlap->width(), m_overlap->height());
displaySurface->blitTo(m_overlap.get(), m_pos.x, m_pos.y, 0, 0,
m_overlap->width(), m_overlap->height());
m_captured = screen;
m_captured = displaySurface;
}
void Overlay::restoreOverlappedArea(const gfx::Rect& restoreBounds)
@ -118,8 +120,7 @@ void Overlay::restoreOverlappedArea(const gfx::Rect& restoreBounds)
m_overlap->blitTo(m_captured, 0, 0, m_pos.x, m_pos.y,
m_overlap->width(), m_overlap->height());
Manager::getDefault()->dirtyRect(bounds());
m_display->dirtyRect(bounds());
m_captured = nullptr;
}

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
@ -20,6 +20,7 @@ namespace os {
}
namespace ui {
class Display;
class Overlay;
using OverlayRef = base::Ref<Overlay>;
@ -31,7 +32,8 @@ namespace ui {
MouseZOrder = 5000
};
Overlay(const os::SurfaceRef& overlaySurface,
Overlay(Display* display,
const os::SurfaceRef& overlaySurface,
const gfx::Point& pos,
ZOrder zorder = NormalZOrder);
~Overlay();
@ -41,7 +43,7 @@ namespace ui {
const gfx::Point& position() const { return m_pos; }
gfx::Rect bounds() const;
void captureOverlappedArea(os::Surface* screen);
void captureOverlappedArea();
void restoreOverlappedArea(const gfx::Rect& restoreBounds);
void drawOverlay();
@ -52,6 +54,7 @@ namespace ui {
}
private:
Display* m_display;
os::SurfaceRef m_surface;
os::SurfaceRef m_overlap;

View File

@ -11,8 +11,6 @@
#include "ui/overlay_manager.h"
#include "os/surface.h"
#include "os/window.h"
#include "ui/manager.h"
#include "ui/overlay.h"
@ -68,11 +66,6 @@ void OverlayManager::restoreOverlappedAreas(const gfx::Rect& restoreBounds)
if (m_overlays.empty())
return;
// TODO can we remove this?
Manager* manager = Manager::getDefault();
if (!manager)
return;
for (auto& overlay : *this)
overlay->restoreOverlappedArea(restoreBounds);
}
@ -82,15 +75,8 @@ void OverlayManager::drawOverlays()
if (m_overlays.empty())
return;
Manager* manager = Manager::getDefault();
if (!manager)
return;
os::Surface* windowSurface = manager->nativeWindow()->surface();
os::SurfaceLock lock(windowSurface);
for (auto& overlay : *this)
overlay->captureOverlappedArea(windowSurface);
overlay->captureOverlappedArea();
for (auto& overlay : *this)
overlay->drawOverlay();

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
@ -13,7 +13,6 @@
#include "base/clamp.h"
#include "os/font.h"
#include "ui/manager.h"
#include "ui/message.h"
#include "ui/size_hint_event.h"
#include "ui/system.h"

View File

@ -36,17 +36,15 @@ namespace ui {
base::thread::native_id_type main_gui_thread;
// Current mouse cursor type.
static CursorType mouse_cursor_type = kOutsideDisplay;
static const Cursor* mouse_cursor_custom = nullptr;
static const Cursor* mouse_cursor = nullptr;
static os::Window* mouse_window = nullptr;
static Display* mouse_display = nullptr;
static OverlayRef mouse_cursor_overlay = nullptr;
static bool use_native_mouse_cursor = true;
static bool support_native_custom_cursor = false;
// Mouse information (button and position).
static gfx::Point m_mouse_pos;
static int mouse_cursor_scale = 1;
@ -58,7 +56,9 @@ static void update_mouse_overlay(const Cursor* cursor)
if (mouse_cursor && mouse_scares == 0) {
if (!mouse_cursor_overlay) {
ASSERT(mouse_display);
mouse_cursor_overlay = base::make_ref<Overlay>(
mouse_display,
mouse_cursor->getSurface(),
get_mouse_position(),
Overlay::MouseZOrder);
@ -83,20 +83,22 @@ static bool update_custom_native_cursor(const Cursor* cursor)
// Check if we can use a custom native mouse in this platform
if (support_native_custom_cursor &&
mouse_window) {
mouse_display) {
os::Window* nativeWindow = mouse_display->nativeWindow();
if (cursor) {
result = mouse_window->setNativeMouseCursor(
result = nativeWindow->setNativeMouseCursor(
// The surface is already scaled by guiscale()
cursor->getSurface().get(),
cursor->getFocus(),
// We scale the cursor by the os::Display scale
mouse_window->scale() * mouse_cursor_scale);
nativeWindow->scale() * mouse_cursor_scale);
}
else if (mouse_cursor_type == kOutsideDisplay) {
result = mouse_window->setNativeMouseCursor(os::NativeCursor::Arrow);
result = nativeWindow->setNativeMouseCursor(os::NativeCursor::Arrow);
}
else {
result = mouse_window->setNativeMouseCursor(os::NativeCursor::Hidden);
result = nativeWindow->setNativeMouseCursor(os::NativeCursor::Hidden);
}
}
@ -146,8 +148,8 @@ static void update_mouse_cursor()
}
// Set native cursor
if (mouse_window) {
bool ok = mouse_window->setNativeMouseCursor(nativeCursor);
if (mouse_display) {
bool ok = mouse_display->nativeWindow()->setNativeMouseCursor(nativeCursor);
// It looks like the specific native cursor is not supported,
// so we can should use the internal overlay (even when we
@ -166,7 +168,8 @@ static void update_mouse_cursor()
}
// Try to use a custom native cursor if it's possible
if (nativeCursor == os::NativeCursor::Hidden &&
if (mouse_display &&
nativeCursor == os::NativeCursor::Hidden &&
!update_custom_native_cursor(cursor)) {
// Or an overlay as last resource
update_mouse_overlay(cursor);
@ -207,7 +210,7 @@ UISystem::~UISystem()
details::exitWidgets();
_internal_set_mouse_window(nullptr);
_internal_set_mouse_display(nullptr);
if (!update_custom_native_cursor(nullptr))
update_mouse_overlay(nullptr);
@ -215,29 +218,24 @@ UISystem::~UISystem()
g_instance = nullptr;
}
void _internal_set_mouse_window(os::Window* window)
void _internal_set_mouse_display(Display* display)
{
CursorType cursor = get_mouse_cursor();
set_mouse_cursor(kNoCursor);
mouse_window = window;
if (window)
mouse_display = display;
if (display)
set_mouse_cursor(cursor); // Restore mouse cursor
}
int display_w()
void _internal_free_mouse_display(Display* display)
{
if (mouse_window)
return mouse_window->width() / mouse_window->scale();
else
return 1;
if (mouse_display == display)
_internal_set_mouse_display(nullptr);
}
int display_h()
gfx::Size get_desktop_size()
{
if (mouse_window)
return mouse_window->height() / mouse_window->scale();
else
return 1;
return Manager::getDefault()->display()->size();
}
void set_clipboard_text(const std::string& text)
@ -331,8 +329,8 @@ const gfx::Point& get_mouse_position()
void set_mouse_position(const gfx::Point& newPos)
{
if (mouse_window)
mouse_window->setMousePosition(newPos);
if (mouse_display)
mouse_display->nativeWindow()->setMousePosition(newPos);
_internal_set_mouse_position(newPos);
}

View File

@ -16,12 +16,11 @@
#include <functional>
#include <string>
namespace os { class Window; }
namespace ui {
class ClipboardDelegate;
class Cursor;
class Display;
class Widget;
class UISystem {
@ -41,8 +40,7 @@ namespace ui {
ClipboardDelegate* m_clipboardDelegate;
};
int display_w();
int display_h();
gfx::Size get_desktop_size();
void set_clipboard_text(const std::string& text);
bool get_clipboard_text(std::string& text);
@ -61,7 +59,8 @@ namespace ui {
void hide_mouse_cursor();
void show_mouse_cursor();
void _internal_set_mouse_window(os::Window* window);
void _internal_set_mouse_display(Display* display);
void _internal_free_mouse_display(Display* display);
void _internal_no_mouse_position();
void _internal_set_mouse_position(const gfx::Point& newPos);

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -122,7 +122,11 @@ void TooltipManager::onTick()
if (!arrowAlign)
target.setOrigin(ui::get_mouse_position()+12*guiscale());
if (m_tipWindow->pointAt(arrowAlign, target)) {
if (m_tipWindow->pointAt(arrowAlign,
target,
m_target.widget->display())) {
// TODO create a native transparent window for the tooltip
m_tipWindow->setDisplay(m_target.widget->display());
m_tipWindow->openWindow();
}
else {
@ -161,7 +165,9 @@ void TipWindow::setCloseOnKeyDown(bool state)
m_closeOnKeyDown = state;
}
bool TipWindow::pointAt(int arrowAlign, const gfx::Rect& target)
bool TipWindow::pointAt(int arrowAlign,
const gfx::Rect& target,
const ui::Display* display)
{
// TODO merge this code with the new ui::fit_bounds() algorithm
@ -212,8 +218,9 @@ bool TipWindow::pointAt(int arrowAlign, const gfx::Rect& target)
break;
}
x = base::clamp(x, 0, ui::display_w()-w);
y = base::clamp(y, 0, ui::display_h()-h);
auto displayBounds = display->bounds();
x = base::clamp(x, displayBounds.x, displayBounds.x2()-w);
y = base::clamp(y, displayBounds.y, displayBounds.y2()-h);
if (m_target.intersects(gfx::Rect(x, y, w, h))) {
switch (trycount) {

View File

@ -1,4 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -18,6 +19,7 @@
namespace ui {
class Display;
class TextBox;
class TipWindow;
@ -70,7 +72,9 @@ namespace ui {
// Returns false there is no enough screen space to show the
// window.
bool pointAt(int arrowAlign, const gfx::Rect& target);
bool pointAt(int arrowAlign,
const gfx::Rect& target,
const ui::Display* display);
TextBox* textBox() const { return m_textBox; }

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -19,6 +19,7 @@
#include "ui/component.h"
#include "ui/cursor.h"
#include "ui/cursor_type.h"
#include "ui/display.h"
#include "ui/entry.h"
#include "ui/event.h"
#include "ui/fit_bounds.h"

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -13,8 +13,8 @@
#include "base/clamp.h"
#include "gfx/size.h"
#include "ui/display.h"
#include "ui/intern.h"
#include "ui/manager.h"
#include "ui/message.h"
#include "ui/move_region.h"
#include "ui/resize_event.h"
@ -28,8 +28,8 @@
#ifdef DEBUG_SCROLL_EVENTS
#include "base/thread.h"
#include "os/display.h"
#include "os/surface.h"
#include "os/window.h"
#endif
#include <algorithm>
@ -289,9 +289,9 @@ void View::onSetViewScroll(const gfx::Point& pt)
// Remove invalid region in the screen (areas that weren't
// re-painted yet)
Manager* manager = this->manager();
if (manager)
validRegion -= manager->getInvalidRegion();
Display* display = this->display();
if (display)
validRegion -= display->getInvalidRegion();
// Add extra regions that cannot be scrolled (this can be customized
// by subclassing ui::View). We use two ScrollRegionEvent, this
@ -327,27 +327,25 @@ void View::onSetViewScroll(const gfx::Point& pt)
invalidRegion -= movable; // Remove the moved region as invalid
movable.offset(-delta);
ui::move_region(manager, movable, delta.x, delta.y);
ui::move_region(display, movable, delta.x, delta.y);
}
#ifdef DEBUG_SCROLL_EVENTS
// Paint invalid region with red fill
{
auto display = manager->getDisplay();
if (display)
display->invalidateRegion(
gfx::Region(gfx::Rect(0, 0, display_w(), display_h())));
if (auto nativeWindow = display->nativeWindow()) {
nativeWindow->invalidateRegion(gfx::Region(display->bounds()));
base::this_thread::sleep_for(0.002);
{
os::Surface* surface = display->getSurface();
os::Surface* surface = nativeWindow->surface();
os::SurfaceLock lock(surface);
os::Paint p;
p.style(os::Paint::Fill);
p.color(gfx::rgba(255, 0, 0));
for (const auto& rc : invalidRegion)
surface->fillRect(gfx::rgba(255, 0, 0), rc);
surface->drawRect(rc, p);
}
if (display)
display->invalidateRegion(
gfx::Region(gfx::Rect(0, 0, display_w(), display_h())));
base::this_thread::sleep_for(0.002);
nativeWindow->invalidateRegion(gfx::Region(display->bounds()));
base::this_thread::sleep_for(0.02);
}
#endif

View File

@ -420,6 +420,14 @@ Manager* Widget::manager() const
return Manager::getDefault();
}
Display* Widget::display() const
{
if (Window* win = this->window())
return win->display();
else
return manager()->display();
}
int Widget::getChildIndex(Widget* child)
{
auto it = std::find(m_children.begin(), m_children.end(), child);
@ -754,36 +762,31 @@ void Widget::getRegion(gfx::Region& region)
void Widget::getDrawableRegion(gfx::Region& region, DrawableRegionFlags flags)
{
Widget* window, *manager, *view;
getRegion(region);
// Cut the top windows areas
if (flags & kCutTopWindows) {
window = this->window();
manager = (window ? window->manager(): nullptr);
Window* window = this->window();
Display* display = this->display();
while (manager) {
const WidgetsList& windows_list = manager->children();
WidgetsList::const_reverse_iterator it =
std::find(windows_list.rbegin(), windows_list.rend(), window);
const auto& uiWindows = display->getWindows();
if (!windows_list.empty() &&
window != windows_list.front() &&
it != windows_list.rend()) {
// Subtract the rectangles
for (++it; it != windows_list.rend(); ++it) {
if (!(*it)->isVisible())
continue;
// Reverse iterator
auto it = std::find(uiWindows.rbegin(),
uiWindows.rend(), window);
Region reg1;
(*it)->getRegion(reg1);
region.createSubtraction(region, reg1);
}
if (!uiWindows.empty() &&
window != uiWindows.front() &&
it != uiWindows.rend()) {
// Subtract the rectangles of each window
for (++it; it != uiWindows.rend(); ++it) {
if (!(*it)->isVisible())
continue;
Region reg1;
(*it)->getRegion(reg1);
region -= reg1;
}
window = manager->window();
manager = (window ? window->manager(): nullptr);
}
}
@ -804,7 +807,7 @@ void Widget::getDrawableRegion(gfx::Region& region, DrawableRegionFlags flags)
else {
reg1.createIntersection(reg2, reg3);
}
region.createSubtraction(region, reg1);
region -= reg1;
}
}
}
@ -813,36 +816,35 @@ void Widget::getDrawableRegion(gfx::Region& region, DrawableRegionFlags flags)
if (!hasFlags(DECORATIVE)) {
Widget* p = this->parent();
while (p) {
region.createIntersection(
region, Region(p->childrenBounds()));
region &= Region(p->childrenBounds());
p = p->parent();
}
}
else {
Widget* p = parent();
if (p) {
region.createIntersection(
region, Region(p->bounds()));
region &= Region(p->bounds());
}
}
// Limit to the manager area
window = this->window();
manager = (window ? window->manager(): nullptr);
{
Window* window = this->window();
Manager* manager = (window ? window->manager(): nullptr);
while (manager) {
View* view = View::getView(manager);
while (manager) {
view = View::getView(manager);
Rect cpos;
if (view)
cpos = static_cast<View*>(view)->viewportBounds();
else
cpos = manager->childrenBounds();
Rect cpos;
if (view)
cpos = static_cast<View*>(view)->viewportBounds();
else
cpos = manager->childrenBounds();
region &= Region(cpos);
region.createIntersection(region, Region(cpos));
window = manager->window();
manager = (window ? window->manager(): nullptr);
window = manager->window();
manager = (window ? window->manager(): nullptr);
}
}
}
@ -980,6 +982,7 @@ void Widget::flushRedraw()
processing.push(this);
}
Display* display = this->display();
Manager* manager = this->manager();
ASSERT(manager);
if (!manager)
@ -1018,13 +1021,14 @@ void Widget::flushRedraw()
for (c=0; c<nrects; ++c, ++it, --count) {
// Create the draw message
msg = new PaintMessage(count, *it);
msg->setDisplay(display);
msg->setRecipient(widget);
// Enqueue the draw message
manager->enqueueMessage(msg);
}
manager->addInvalidRegion(widget->m_updateRegion);
display->addInvalidRegion(widget->m_updateRegion);
widget->m_updateRegion.clear();
}
}
@ -1040,6 +1044,8 @@ void Widget::paint(Graphics* graphics,
std::queue<Widget*> processing;
processing.push(this);
Display* display = this->display();
while (!processing.empty()) {
Widget* widget = processing.front();
processing.pop();
@ -1059,17 +1065,17 @@ void Widget::paint(Graphics* graphics,
region.createIntersection(region, drawRegion);
Graphics graphics2(
display,
base::AddRef(graphics->getInternalSurface()),
widget->bounds().x,
widget->bounds().y);
graphics2.setFont(AddRef(widget->font()));
for (Region::const_iterator
it = region.begin(),
end = region.end(); it != end; ++it) {
IntersectClip clip(&graphics2, Rect(*it).offset(
-widget->bounds().x,
-widget->bounds().y));
for (const gfx::Rect& rc : region) {
IntersectClip clip(&graphics2,
Rect(rc).offset(
-widget->bounds().x,
-widget->bounds().y));
widget->paintEvent(&graphics2, isBg);
}
}
@ -1089,14 +1095,22 @@ bool Widget::paintEvent(Graphics* graphics,
enableFlags(HIDDEN);
if (parent()) {
gfx::Region rgn(parent()->bounds());
rgn.createIntersection(
rgn,
gfx::Region(
if (parent()->display() == display()) {
gfx::Region rgn(parent()->bounds());
rgn &= gfx::Region(
graphics->getClipBounds().offset(
graphics->getInternalDeltaX(),
graphics->getInternalDeltaY())));
parent()->paint(graphics, rgn, true);
graphics->getInternalDeltaY()));
parent()->paint(graphics, rgn, true);
}
else {
// TODO clear surface with transparent color, the following
// line doesn't work because we have to specify the
// SkBlendMode::kSrc mode instead of
// SkBlendMode::kSrcOver
//graphics->fillRect(gfx::rgba(0, 0, 0, 0), clientBounds());
}
}
disableFlags(HIDDEN);
@ -1152,17 +1166,19 @@ void Widget::invalidateRegion(const Region& region)
class DeleteGraphicsAndSurface {
public:
DeleteGraphicsAndSurface(const gfx::Rect& clip,
const os::SurfaceRef& surface)
: m_pt(clip.origin()), m_surface(surface) {
const os::SurfaceRef& surface,
os::SurfaceRef& dst)
: m_pt(clip.origin())
, m_surface(surface)
, m_dst(dst) {
}
void operator()(Graphics* graphics) {
{
os::Surface* dst = os::instance()->defaultWindow()->surface();
os::SurfaceLock lockSrc(m_surface.get());
os::SurfaceLock lockDst(dst);
os::SurfaceLock lockDst(m_dst.get());
m_surface->blitTo(
dst, 0, 0, m_pt.x, m_pt.y,
m_dst.get(), 0, 0, m_pt.x, m_pt.y,
m_surface->width(), m_surface->height());
}
m_surface.reset();
@ -1172,25 +1188,27 @@ public:
private:
gfx::Point m_pt;
os::SurfaceRef m_surface;
os::SurfaceRef m_dst;
};
GraphicsPtr Widget::getGraphics(const gfx::Rect& clip)
{
GraphicsPtr graphics;
os::SurfaceRef surface;
os::Surface* defaultSurface = os::instance()->defaultWindow()->surface();
Display* display = this->display();
os::SurfaceRef dstSurface = AddRef(display->surface());
// In case of double-buffering, we need to create the temporary
// buffer only if the default surface is the screen.
if (isDoubleBuffered() && defaultSurface->isDirectToScreen()) {
surface = os::instance()->makeSurface(clip.w, clip.h);
graphics.reset(new Graphics(surface, -clip.x, -clip.y),
DeleteGraphicsAndSurface(clip, surface));
if (isDoubleBuffered() && dstSurface->isDirectToScreen()) {
os::SurfaceRef surface =
os::instance()->makeSurface(clip.w, clip.h);
graphics.reset(new Graphics(display, surface, -clip.x, -clip.y),
DeleteGraphicsAndSurface(clip, surface,
dstSurface));
}
// In other case, we can draw directly onto the screen.
else {
surface = AddRef(defaultSurface);
graphics.reset(new Graphics(surface, bounds().x, bounds().y));
graphics.reset(new Graphics(display, dstSurface, bounds().x, bounds().y));
}
graphics->setFont(AddRef(font()));

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -29,6 +29,7 @@
namespace ui {
class Display;
class InitThemeEvent;
class KeyMessage;
class LoadLayoutEvent;
@ -161,6 +162,7 @@ namespace ui {
Window* window() const;
Widget* parent() const { return m_parent; }
Manager* manager() const;
Display* display() const;
// Returns a list of children.
const WidgetsList& children() const { return m_children; }

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -13,6 +13,7 @@
#include "gfx/size.h"
#include "ui/button.h"
#include "ui/display.h"
#include "ui/graphics.h"
#include "ui/intern.h"
#include "ui/label.h"
@ -107,6 +108,7 @@ protected:
Window::Window(Type type, const std::string& text)
: Widget(kWindowWidget)
, m_display(nullptr)
, m_closer(nullptr)
, m_titleLabel(nullptr)
, m_closeButton(nullptr)
@ -133,6 +135,27 @@ Window::~Window()
manager()->_closeWindow(this, isVisible());
}
Display* Window::display() const
{
if (m_display)
return m_display;
else if (auto man = manager())
return man->display();
else
return nullptr;
}
void Window::setDisplay(Display* display)
{
if (m_display)
m_display->removeWindow(this);
m_display = display;
if (m_display)
m_display->addWindow(this);
}
void Window::setAutoRemap(bool state)
{
m_isAutoRemap = state;
@ -658,12 +681,13 @@ void Window::moveWindow(const gfx::Rect& rect, bool use_blit)
moveableRegion.createIntersection(oldDrawableRegion, reg1);
// Move the window's graphics
ScreenGraphics g;
Display* display = this->display();
ScreenGraphics g(display);
hide_mouse_cursor();
{
IntersectClip clip(&g, man_pos);
if (clip) {
ui::move_region(manager, moveableRegion, dx, dy);
ui::move_region(display, moveableRegion, dx, dy);
}
}
show_mouse_cursor();

View File

@ -1,4 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -27,6 +28,9 @@ namespace ui {
explicit Window(Type type, const std::string& text = "");
~Window();
Display* display() const;
void setDisplay(Display* display);
Widget* closer() const { return m_closer; }
void setAutoRemap(bool state);
@ -77,6 +81,7 @@ namespace ui {
void limitSize(int* w, int* h);
void moveWindow(const gfx::Rect& rect, bool use_blit);
Display* m_display;
Widget* m_closer;
Label* m_titleLabel;
ButtonBase* m_closeButton;