Fix several issues locating windows with multiple UI windows

We've refactored the code to support locating windows (and popups
windows + hot regions) correctly in both modes: with one ui::Display
and with multiple ui::Displays.

For this we've added a new ui::fit_bounds() function that works in
both modes.
This commit is contained in:
David Capello 2021-03-19 18:57:56 -03:00
parent 7079801697
commit bcd69495ce
48 changed files with 508 additions and 254 deletions

2
laf

@ -1 +1 @@
Subproject commit d290eaf37fcb1a48cee433e6b9a1b73854671df0 Subproject commit 1106d7488dcf1410815601c9eaa38407bc243893

View File

@ -342,12 +342,17 @@ void CanvasSizeCommand::onExecute(Context* context)
// Find best position for the window on the editor // Find best position for the window on the editor
if (DocView* docView = static_cast<UIContext*>(context)->activeView()) { if (DocView* docView = static_cast<UIContext*>(context)->activeView()) {
window->positionWindow( Display* display = ui::Manager::getDefault()->display();
docView->bounds().x2() - window->bounds().w, ui::fit_bounds(display,
docView->bounds().y); window.get(),
gfx::Rect(docView->bounds().x2() - window->bounds().w,
docView->bounds().y,
window->bounds().w,
window->bounds().h));
} }
else else {
window->centerWindow(); window->centerWindow();
}
load_window_pos(window.get(), "CanvasSize"); load_window_pos(window.get(), "CanvasSize");
window->setVisible(true); window->setVisible(true);

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A. // Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -246,10 +246,7 @@ public:
advancedCheck()->Click.connect( advancedCheck()->Click.connect(
[this](ui::Event&){ [this](ui::Event&){
advanced()->setVisible(advancedCheck()->isSelected()); advanced()->setVisible(advancedCheck()->isSelected());
expandWindow(sizeHint());
const gfx::Rect origBounds = bounds();
setBounds(gfx::Rect(bounds().origin(), sizeHint()));
manager()->invalidateRect(origBounds);
}); });
} }
else { else {

View File

@ -32,6 +32,7 @@
#include "base/string.h" #include "base/string.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "ui/alert.h" #include "ui/alert.h"
#include "ui/fit_bounds.h"
#include "ui/graphics.h" #include "ui/graphics.h"
#include "ui/listitem.h" #include "ui/listitem.h"
#include "ui/message.h" #include "ui/message.h"
@ -882,11 +883,19 @@ void KeyboardShortcutsCommand::onExecute(Context* context)
std::string neededSearchCopy = m_search; std::string neededSearchCopy = m_search;
KeyboardShortcutsWindow window(keys, menuKeys, neededSearchCopy); KeyboardShortcutsWindow window(keys, menuKeys, neededSearchCopy);
gfx::Size displaySize = ui::get_desktop_size(); ui::Display* mainDisplay = Manager::getDefault()->display();
window.setBounds(gfx::Rect(0, 0, displaySize.w*3/4, displaySize.h*3/4)); ui::fit_bounds(mainDisplay, &window,
gfx::Rect(mainDisplay->size()),
[](const gfx::Rect& workarea,
gfx::Rect& bounds,
std::function<gfx::Rect(Widget*)> getWidgetBounds) {
gfx::Point center = bounds.center();
bounds.setSize(workarea.size()*3/4);
bounds.setOrigin(center - gfx::Point(bounds.size()/2));
});
window.loadLayout(); window.loadLayout();
window.centerWindow();
window.setVisible(true); window.setVisible(true);
window.openWindowInForeground(); window.openWindowInForeground();

View File

@ -124,11 +124,7 @@ private:
} }
if (!m_fontPopup->isVisible()) { if (!m_fontPopup->isVisible()) {
gfx::Size displaySize = manager()->display()->size(); m_fontPopup->showPopup(display(), fontFace()->bounds());
gfx::Rect bounds = fontFace()->bounds();
m_fontPopup->showPopup(
gfx::Rect(bounds.x, bounds.y+bounds.h,
displaySize.w/2, displaySize.h/2));
} }
else { else {
m_fontPopup->closeWindow(NULL); m_fontPopup->closeWindow(NULL);

View File

@ -71,14 +71,15 @@ public:
void centerConsole() { void centerConsole() {
initTheme(); initTheme();
remapWindow(); Display* display = ui::Manager::getDefault()->display();
const gfx::Rect displayRc = display->bounds();
gfx::Rect rc;
rc.w = displayRc.w*9/10;
rc.h = displayRc.h*6/10;
rc.x = displayRc.x + displayRc.w/2 - rc.w/2;
rc.y = displayRc.y + displayRc.h/2 - rc.h/2;
// TODO center to main window or screen workspace ui::fit_bounds(display, this, rc);
gfx::Size displaySize = manager()->display()->size();
setBounds(gfx::Rect(0, 0, displaySize.w*9/10, displaySize.h*6/10));
centerWindow();
invalidate();
} }
private: private:

View File

@ -693,11 +693,15 @@ void CustomizedGuiManager::onInitTheme(InitThemeEvent& ev)
void CustomizedGuiManager::onNewDisplayConfiguration(Display* display) void CustomizedGuiManager::onNewDisplayConfiguration(Display* display)
{ {
Manager::onNewDisplayConfiguration(display); Manager::onNewDisplayConfiguration(display);
save_gui_config();
// TODO Should we provide a more generic way for all ui::Window to // Only whne the main display/window is modified
// detect the os::Window (or UI Screen Scaling) change? if (display == this->display()) {
Console::notifyNewDisplayConfiguration(); save_gui_config();
// TODO Should we provide a more generic way for all ui::Window to
// detect the os::Window (or UI Screen Scaling) change?
Console::notifyNewDisplayConfiguration();
}
} }
std::string CustomizedGuiManager::loadLayout(Widget* widget) std::string CustomizedGuiManager::loadLayout(Widget* widget)

View File

@ -589,7 +589,7 @@ void BrowserView::onTabPopup(Workspace* workspace)
if (!menu) if (!menu)
return; return;
menu->showPopup(mousePosInDisplay()); menu->showPopup(mousePosInDisplay(), display());
} }
} // namespace app } // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -36,6 +36,7 @@
#include "os/surface.h" #include "os/surface.h"
#include "os/system.h" #include "os/system.h"
#include "ui/button.h" #include "ui/button.h"
#include "ui/fit_bounds.h"
#include "ui/link_label.h" #include "ui/link_label.h"
#include "ui/listitem.h" #include "ui/listitem.h"
#include "ui/menu.h" #include "ui/menu.h"
@ -52,22 +53,24 @@ using namespace ui;
namespace { namespace {
void show_popup_menu(PopupWindow* popupWindow, Menu* popupMenu, void show_popup_menu(PopupWindow* popupWindow,
const gfx::Point& pt) Menu* popupMenu,
const gfx::Point& pt,
Display* display)
{ {
// Here we make the popup window temporaly floating, so it's // Here we make the popup window temporaly floating, so it's
// not closed by the popup menu. // not closed by the popup menu.
popupWindow->makeFloating(); popupWindow->makeFloating();
popupMenu->showPopup(pt); popupMenu->showPopup(pt, display);
// Add the menu popup region to the window popup hot region so it's // Add the menu popup region to the window popup hot region so it's
// not closed after we close the menu. // not closed after we close the menu.
popupWindow->makeFixed(); popupWindow->makeFixed();
gfx::Region rgn; gfx::Region rgn;
rgn.createUnion(gfx::Region(popupWindow->bounds()), rgn.createUnion(gfx::Region(popupWindow->boundsOnScreen()),
gfx::Region(popupMenu->bounds())); gfx::Region(popupMenu->boundsOnScreen()));
popupWindow->setHotRegion(rgn); popupWindow->setHotRegion(rgn);
} }
@ -194,7 +197,8 @@ private:
m_changeFlags = true; m_changeFlags = true;
show_popup_menu(m_popup, &menu, show_popup_menu(m_popup, &menu,
gfx::Point(origin().x, origin().y+bounds().h)); gfx::Point(origin().x, origin().y+bounds().h),
display());
if (m_changeFlags) { if (m_changeFlags) {
brush = m_brushes.getBrushSlot(m_slot); brush = m_brushes.getBrushSlot(m_slot);
@ -299,8 +303,10 @@ private:
params.shade()->setSelected(saveBrush.shade()); params.shade()->setSelected(saveBrush.shade());
params.pixelPerfect()->setSelected(saveBrush.pixelPerfect()); params.pixelPerfect()->setSelected(saveBrush.pixelPerfect());
show_popup_menu(static_cast<PopupWindow*>(window()), &menu, show_popup_menu(static_cast<PopupWindow*>(window()),
gfx::Point(origin().x, origin().y+bounds().h)); &menu,
gfx::Point(origin().x, origin().y+bounds().h),
display());
// Save preferences // Save preferences
if (saveBrush.brushType() != params.brushType()->isSelected()) if (saveBrush.brushType() != params.brushType()->isSelected())
@ -384,7 +390,8 @@ void BrushPopup::setBrush(Brush* brush)
} }
} }
void BrushPopup::regenerate(const gfx::Rect& box) void BrushPopup::regenerate(ui::Display* display,
const gfx::Point& pos)
{ {
auto& brushSlots = App::instance()->brushes().getBrushSlots(); auto& brushSlots = App::instance()->brushes().getBrushSlots();
@ -425,8 +432,8 @@ void BrushPopup::regenerate(const gfx::Rect& box)
m_box.addChild(m_customBrushes); m_box.addChild(m_customBrushes);
// Resize the window and change the hot region. // Resize the window and change the hot region.
setBounds(gfx::Rect(box.origin(), sizeHint())); fit_bounds(display, this, gfx::Rect(pos, sizeHint()));
setHotRegion(gfx::Region(bounds())); setHotRegion(gfx::Region(boundsOnScreen()));
} }
void BrushPopup::onBrushChanges() void BrushPopup::onBrushChanges()
@ -435,7 +442,9 @@ void BrushPopup::onBrushChanges()
gfx::Region rgn; gfx::Region rgn;
getDrawableRegion(rgn, kCutTopWindowsAndUseChildArea); getDrawableRegion(rgn, kCutTopWindowsAndUseChildArea);
regenerate(bounds()); Display* mainDisplay = manager()->display();
regenerate(mainDisplay,
mainDisplay->nativeWindow()->pointFromScreen(boundsOnScreen().origin()));
invalidate(); invalidate();
parent()->invalidateRegion(rgn); parent()->invalidateRegion(rgn);

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello // Copyright (C) 2001-2015 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -21,7 +21,8 @@ namespace app {
BrushPopup(); BrushPopup();
void setBrush(doc::Brush* brush); void setBrush(doc::Brush* brush);
void regenerate(const gfx::Rect& box); void regenerate(ui::Display* display,
const gfx::Point& pos);
static os::SurfaceRef createSurfaceForBrush(const doc::BrushRef& brush); static os::SurfaceRef createSurfaceForBrush(const doc::BrushRef& brush);

View File

@ -1868,7 +1868,7 @@ void ColorBar::showPaletteSortOptions()
asc.Click.connect([this]{ setAscending(true); }); asc.Click.connect([this]{ setAscending(true); });
des.Click.connect([this]{ setAscending(false); }); des.Click.connect([this]{ setAscending(false); });
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h)); menu.showPopup(gfx::Point(bounds.x, bounds.y2()), display());
} }
void ColorBar::showPalettePresets() void ColorBar::showPalettePresets()
@ -1884,16 +1884,13 @@ void ColorBar::showPalettePresets()
} }
if (!m_palettePopup->isVisible()) { if (!m_palettePopup->isVisible()) {
gfx::Size displaySize = ui::get_desktop_size();
gfx::Rect bounds = m_buttons.getItem( gfx::Rect bounds = m_buttons.getItem(
static_cast<int>(PalButton::PRESETS))->bounds(); static_cast<int>(PalButton::PRESETS))->bounds();
m_palettePopup->showPopup( m_palettePopup->showPopup(display(), bounds);
gfx::Rect(bounds.x, bounds.y+bounds.h,
displaySize.w/2, displaySize.h*3/4));
} }
else { else {
m_palettePopup->closeWindow(NULL); m_palettePopup->closeWindow(nullptr);
} }
} }
@ -1904,7 +1901,7 @@ void ColorBar::showPaletteOptions()
gfx::Rect bounds = m_buttons.getItem( gfx::Rect bounds = m_buttons.getItem(
static_cast<int>(PalButton::OPTIONS))->bounds(); static_cast<int>(PalButton::OPTIONS))->bounds();
menu->showPopup(gfx::Point(bounds.x, bounds.y+bounds.h)); menu->showPopup(gfx::Point(bounds.x, bounds.y2()), display());
} }
} }

View File

@ -53,6 +53,7 @@ ColorButton::ColorButton(const app::Color& color,
, m_color(color) , m_color(color)
, m_pixelFormat(pixelFormat) , m_pixelFormat(pixelFormat)
, m_window(nullptr) , m_window(nullptr)
, m_desktopCoords(false)
, m_dependOnLayer(false) , m_dependOnLayer(false)
, m_options(options) , m_options(options)
{ {
@ -306,9 +307,12 @@ void ColorButton::onLoadLayout(ui::LoadLayoutEvent& ev)
{ {
if (canPin()) { if (canPin()) {
bool pinned = false; bool pinned = false;
m_desktopCoords = false;
ev.stream() >> pinned; ev.stream() >> pinned;
if (ev.stream() && pinned) if (ev.stream() && pinned)
ev.stream() >> m_windowDefaultBounds; ev.stream() >> m_windowDefaultBounds
>> m_desktopCoords;
m_hiddenPopupBounds = m_windowDefaultBounds; m_hiddenPopupBounds = m_windowDefaultBounds;
} }
@ -316,8 +320,12 @@ void ColorButton::onLoadLayout(ui::LoadLayoutEvent& ev)
void ColorButton::onSaveLayout(ui::SaveLayoutEvent& ev) void ColorButton::onSaveLayout(ui::SaveLayoutEvent& ev)
{ {
if (canPin() && m_window && m_window->isPinned()) if (canPin() && m_window && m_window->isPinned()) {
ev.stream() << 1 << ' ' << m_window->bounds(); if (m_desktopCoords)
ev.stream() << 1 << ' ' << m_window->lastNativeFrame() << ' ' << 1;
else
ev.stream() << 1 << ' ' << m_window->bounds() << ' ' << 0;
}
else else
ev.stream() << 0; ev.stream() << 0;
} }
@ -339,37 +347,39 @@ void ColorButton::openPopup(const bool forcePinned)
} }
m_window->setColor(m_color, ColorPopup::ChangeType); m_window->setColor(m_color, ColorPopup::ChangeType);
m_window->remapWindow();
fit_bounds(
display(),
m_window,
gfx::Rect(m_window->sizeHint()),
[this, pinned, forcePinned](const gfx::Rect& workarea,
gfx::Rect& winBounds,
std::function<gfx::Rect(Widget*)> getWidgetBounds) {
if (!pinned || (forcePinned && m_hiddenPopupBounds.isEmpty())) {
gfx::Rect bounds = getWidgetBounds(this);
winBounds.x = base::clamp(bounds.x, workarea.x, workarea.x2()-winBounds.w);
if (bounds.y2()+winBounds.h <= workarea.y2())
winBounds.y = std::max(0, bounds.y2());
else
winBounds.y = std::max(0, bounds.y-winBounds.h);
}
else if (forcePinned) {
winBounds = convertBounds(m_hiddenPopupBounds);
}
else {
winBounds = convertBounds(m_windowDefaultBounds);
}
});
m_window->openWindow(); 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, 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);
}
else if (forcePinned) {
winBounds = m_hiddenPopupBounds;
}
else {
winBounds = m_windowDefaultBounds;
}
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();
m_window->layout();
m_window->setPinned(pinned); m_window->setPinned(pinned);
// Add the ColorButton area to the ColorPopup hot-region // Add the ColorButton area to the ColorPopup hot-region
if (!pinned) { if (!pinned) {
gfx::Rect rc = bounds().createUnion(m_window->bounds()); gfx::Rect rc = boundsOnScreen().createUnion(m_window->boundsOnScreen());
rc.enlarge(8); rc.enlarge(8);
gfx::Region rgn(rc); gfx::Region rgn(rc);
static_cast<PopupWindow*>(m_window)->setHotRegion(rgn); static_cast<PopupWindow*>(m_window)->setHotRegion(rgn);
@ -386,7 +396,14 @@ void ColorButton::closePopup()
void ColorButton::onWindowClose(ui::CloseEvent& ev) void ColorButton::onWindowClose(ui::CloseEvent& ev)
{ {
m_hiddenPopupBounds = m_window->bounds(); if (get_multiple_displays()) {
m_desktopCoords = true;
m_hiddenPopupBounds = m_window->lastNativeFrame();
}
else {
m_desktopCoords = false;
m_hiddenPopupBounds = m_window->bounds();
}
} }
void ColorButton::onWindowColorChange(const app::Color& color) void ColorButton::onWindowColorChange(const app::Color& color)
@ -418,4 +435,23 @@ void ColorButton::onActiveSiteChange(const Site& site)
} }
} }
gfx::Rect ColorButton::convertBounds(const gfx::Rect& bounds) const
{
// Convert to desktop
if (get_multiple_displays() && !m_desktopCoords) {
auto nativeWindow = display()->nativeWindow();
return gfx::Rect(nativeWindow->pointToScreen(bounds.origin()),
nativeWindow->pointToScreen(bounds.point2()));
}
// Convert to display
else if (!get_multiple_displays() && m_desktopCoords) {
auto nativeWindow = display()->nativeWindow();
return gfx::Rect(nativeWindow->pointFromScreen(bounds.origin()),
nativeWindow->pointFromScreen(bounds.point2()));
}
// No conversion is required
else
return bounds;
}
} // namespace app } // namespace app

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -68,11 +69,17 @@ namespace app {
void onWindowColorChange(const app::Color& color); void onWindowColorChange(const app::Color& color);
bool canPin() const { return m_options.canPinSelector; } bool canPin() const { return m_options.canPinSelector; }
// Used to convert saved bounds (m_window/hiddenDefaultBounds,
// which can be relative to the display or relative to the screen)
// to the current system of coordinates.
gfx::Rect convertBounds(const gfx::Rect& bounds) const;
app::Color m_color; app::Color m_color;
PixelFormat m_pixelFormat; PixelFormat m_pixelFormat;
ColorPopup* m_window; ColorPopup* m_window;
gfx::Rect m_windowDefaultBounds; gfx::Rect m_windowDefaultBounds;
gfx::Rect m_hiddenPopupBounds; gfx::Rect m_hiddenPopupBounds;
bool m_desktopCoords; // True if m_windowDefault/hiddenPopupBounds are screen coordinates
bool m_dependOnLayer; bool m_dependOnLayer;
ColorButtonOptions m_options; ColorButtonOptions m_options;
}; };

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -412,7 +412,7 @@ void ColorWheel::onOptions()
} }
gfx::Rect rc = m_options.bounds(); gfx::Rect rc = m_options.bounds();
menu.showPopup(gfx::Point(rc.x+rc.w, rc.y)); menu.showPopup(gfx::Point(rc.x2(), rc.y), display());
} }
int ColorWheel::convertHueAngle(int hue, int dir) const int ColorWheel::convertHueAngle(int hue, int dir) const

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -66,6 +66,7 @@
#include "render/ordered_dither.h" #include "render/ordered_dither.h"
#include "ui/button.h" #include "ui/button.h"
#include "ui/combobox.h" #include "ui/combobox.h"
#include "ui/fit_bounds.h"
#include "ui/int_entry.h" #include "ui/int_entry.h"
#include "ui/label.h" #include "ui/label.h"
#include "ui/listbox.h" #include "ui/listbox.h"
@ -209,20 +210,19 @@ protected:
private: private:
// Returns a little rectangle that can be used by the popup as the // Returns a little rectangle that can be used by the popup as the
// first brush position. // first brush position.
gfx::Rect getPopupBox() { gfx::Point popupPosCandidate() const {
Rect rc = bounds(); Rect rc = bounds();
rc.y += rc.h - 2*guiscale(); rc.y += rc.h - 2*guiscale();
rc.setSize(sizeHint()); return rc.origin();
return rc;
} }
void openPopup() { void openPopup() {
doc::BrushRef brush = m_owner->activeBrush(); doc::BrushRef brush = m_owner->activeBrush();
m_popupWindow.regenerate(getPopupBox()); m_popupWindow.regenerate(display(), popupPosCandidate());
m_popupWindow.setBrush(brush.get()); m_popupWindow.setBrush(brush.get());
Region rgn(m_popupWindow.bounds().createUnion(bounds())); Region rgn(m_popupWindow.boundsOnScreen().createUnion(boundsOnScreen()));
m_popupWindow.setHotRegion(rgn); m_popupWindow.setHotRegion(rgn);
m_popupWindow.openWindow(); m_popupWindow.openWindow();
@ -450,7 +450,8 @@ protected:
} }
}); });
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h)); menu.showPopup(gfx::Point(bounds.x, bounds.y2()),
display());
deselectItems(); deselectItems();
} }
@ -502,7 +503,8 @@ protected:
AppMenus::instance() AppMenus::instance()
->getInkPopupMenu() ->getInkPopupMenu()
->showPopup(gfx::Point(bounds.x, bounds.y+bounds.h)); ->showPopup(gfx::Point(bounds.x, bounds.y2()),
display());
deselectItems(); deselectItems();
} }
@ -627,7 +629,8 @@ private:
} }
} }
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h)); menu.showPopup(gfx::Point(bounds.x, bounds.y2()),
display());
m_button.invalidate(); m_button.invalidate();
} }
@ -808,7 +811,8 @@ private:
masked.Click.connect([this]{ setOpaque(false); }); masked.Click.connect([this]{ setOpaque(false); });
automatic.Click.connect([this]{ onAutomatic(); }); automatic.Click.connect([this]{ onAutomatic(); });
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h)); menu.showPopup(gfx::Point(bounds.x, bounds.y2()),
display());
} }
void onChangeColor() { void onChangeColor() {
@ -905,7 +909,8 @@ private:
app::gen::PivotPosition(buttonset.selectedItem())); app::gen::PivotPosition(buttonset.selectedItem()));
}); });
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h)); menu.showPopup(gfx::Point(bounds.x, bounds.y2()),
display());
} }
void onPivotChange() { void onPivotChange() {
@ -1151,8 +1156,10 @@ public:
const gfx::Rect bounds = this->bounds(); const gfx::Rect bounds = this->bounds();
m_popup->remapWindow(); m_popup->remapWindow();
m_popup->positionWindow(bounds.x, bounds.y+bounds.h); fit_bounds(display(), m_popup.get(),
m_popup->setHotRegion(gfx::Region(m_popup->bounds())); gfx::Rect(gfx::Point(bounds.x, bounds.y2()),
m_popup->sizeHint()));
m_popup->setHotRegion(gfx::Region(m_popup->boundsOnScreen()));
m_popup->openWindow(); m_popup->openWindow();
} }
@ -1436,7 +1443,8 @@ private:
doc->notifyGeneralUpdate(); doc->notifyGeneralUpdate();
}); });
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h)); menu.showPopup(gfx::Point(bounds.x, bounds.y2()),
display());
} }
} }
}; };

View File

@ -413,7 +413,7 @@ void DataRecoveryView::onTabPopup(Workspace* workspace)
if (!menu) if (!menu)
return; return;
menu->showPopup(mousePosInDisplay()); menu->showPopup(mousePosInDisplay(), display());
} }
void DataRecoveryView::onOpen() void DataRecoveryView::onOpen()
@ -461,7 +461,7 @@ void DataRecoveryView::onOpenMenu()
rawFrames.Click.connect([this]{ onOpenRaw(crash::RawImagesAs::kFrames); }); rawFrames.Click.connect([this]{ onOpenRaw(crash::RawImagesAs::kFrames); });
rawLayers.Click.connect([this]{ onOpenRaw(crash::RawImagesAs::kLayers); }); rawLayers.Click.connect([this]{ onOpenRaw(crash::RawImagesAs::kLayers); });
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h)); menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h), display());
} }
void DataRecoveryView::onDelete() void DataRecoveryView::onDelete()

View File

@ -140,7 +140,7 @@ void DevConsoleView::onTabPopup(Workspace* workspace)
if (!menu) if (!menu)
return; return;
menu->showPopup(mousePosInDisplay()); menu->showPopup(mousePosInDisplay(), display());
} }
bool DevConsoleView::onProcessMessage(Message* msg) bool DevConsoleView::onProcessMessage(Message* msg)

View File

@ -362,7 +362,7 @@ void DocView::onTabPopup(Workspace* workspace)
ctx->setActiveView(this); ctx->setActiveView(this);
ctx->updateFlags(); ctx->updateFlags();
menu->showPopup(mousePosInDisplay()); menu->showPopup(mousePosInDisplay(), display());
} }
bool DocView::onProcessMessage(Message* msg) bool DocView::onProcessMessage(Message* msg)

View File

@ -370,15 +370,10 @@ void DynamicsPopup::onValuesChange(ButtonSet::Item* item)
m_dynamics->velocityLabel()->setVisible(hasVelocity); m_dynamics->velocityLabel()->setVisible(hasVelocity);
m_dynamics->velocityPlaceholder()->setVisible(hasVelocity); m_dynamics->velocityPlaceholder()->setVisible(hasVelocity);
auto oldBounds = bounds(); expandWindow(sizeHint());
layout();
setBounds(gfx::Rect(origin(), sizeHint()));
m_hotRegion |= gfx::Region(bounds()); m_hotRegion |= gfx::Region(boundsOnScreen());
setHotRegion(m_hotRegion); setHotRegion(m_hotRegion);
if (isVisible())
manager()->invalidateRect(oldBounds);
} }
void DynamicsPopup::updateFromToText() void DynamicsPopup::updateFromToText()
@ -400,7 +395,7 @@ bool DynamicsPopup::onProcessMessage(Message* msg)
switch (msg->type()) { switch (msg->type()) {
case kOpenMessage: case kOpenMessage:
m_hotRegion = gfx::Region(bounds()); m_hotRegion = gfx::Region(boundsOnScreen());
setHotRegion(m_hotRegion); setHotRegion(m_hotRegion);
manager()->addMessageFilter(kMouseMoveMessage, this); manager()->addMessageFilter(kMouseMoveMessage, this);
manager()->addMessageFilter(kMouseDownMessage, this); manager()->addMessageFilter(kMouseDownMessage, this);
@ -442,8 +437,13 @@ bool DynamicsPopup::onProcessMessage(Message* msg)
} }
case kMouseDownMessage: { case kMouseDownMessage: {
if (!msg->display())
break;
os::Window* nativeWindow = msg->display()->nativeWindow();
auto mouseMsg = static_cast<MouseMessage*>(msg); auto mouseMsg = static_cast<MouseMessage*>(msg);
auto picked = manager()->pick(mouseMsg->position()); auto screenPos = nativeWindow->pointToScreen(mouseMsg->position());
auto picked = manager()->pickFromScreenPos(screenPos);
if ((picked == nullptr) || if ((picked == nullptr) ||
(picked->window() != this && (picked->window() != this &&
picked->window() != m_ditheringSel->getWindowWidget())) { picked->window() != m_ditheringSel->getWindowWidget())) {
@ -451,6 +451,7 @@ bool DynamicsPopup::onProcessMessage(Message* msg)
} }
break; break;
} }
} }
return PopupWindow::onProcessMessage(msg); return PopupWindow::onProcessMessage(msg);
} }

View File

@ -2754,7 +2754,7 @@ void Editor::showAnimationSpeedMultiplierPopup(Option<bool>& playOnce,
menu.addChild(item); menu.addChild(item);
} }
menu.showPopup(mousePosInDisplay()); menu.showPopup(mousePosInDisplay(), display());
if (isPlaying()) { if (isPlaying()) {
// Re-play // Re-play

View File

@ -256,7 +256,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
if (!editor->hasSelectedSlices()) if (!editor->hasSelectedSlices())
params.set("id", base::convert_to<std::string>(hit.slice()->id()).c_str()); params.set("id", base::convert_to<std::string>(hit.slice()->id()).c_str());
AppMenuItem::setContextParams(params); AppMenuItem::setContextParams(params);
popupMenu->showPopup(msg->position()); popupMenu->showPopup(msg->position(), editor->display());
AppMenuItem::setContextParams(Params()); AppMenuItem::setContextParams(Params());
} }
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -29,6 +29,7 @@
#include "os/system.h" #include "os/system.h"
#include "ui/box.h" #include "ui/box.h"
#include "ui/button.h" #include "ui/button.h"
#include "ui/fit_bounds.h"
#include "ui/graphics.h" #include "ui/graphics.h"
#include "ui/listitem.h" #include "ui/listitem.h"
#include "ui/paint_event.h" #include "ui/paint_event.h"
@ -183,15 +184,23 @@ FontPopup::FontPopup()
m_listBox.addChild(new ListItem("No system fonts were found")); m_listBox.addChild(new ListItem("No system fonts were found"));
} }
void FontPopup::showPopup(const gfx::Rect& bounds) void FontPopup::showPopup(Display* display,
const gfx::Rect& buttonBounds)
{ {
m_popup->loadFont()->setEnabled(false); m_popup->loadFont()->setEnabled(false);
m_listBox.selectChild(NULL); m_listBox.selectChild(NULL);
moveWindow(bounds); ui::fit_bounds(display, this,
gfx::Rect(buttonBounds.x, buttonBounds.y2(), 32, 32),
[](const gfx::Rect& workarea,
gfx::Rect& bounds,
std::function<gfx::Rect(Widget*)> getWidgetBounds) {
bounds.w = workarea.w / 2;
bounds.h = workarea.h / 2;
});
// Setup the hot-region // Setup the hot-region
setHotRegion(gfx::Region(gfx::Rect(bounds).enlarge(32 * guiscale()))); setHotRegion(gfx::Region(gfx::Rect(boundsOnScreen()).enlarge(32*guiscale()*display->scale())));
openWindow(); openWindow();
} }

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -26,7 +27,8 @@ namespace app {
public: public:
FontPopup(); FontPopup();
void showPopup(const gfx::Rect& bounds); void showPopup(ui::Display* display,
const gfx::Rect& buttonBounds);
obs::signal<void(const std::string&)> Load; obs::signal<void(const std::string&)> Load;

View File

@ -136,7 +136,7 @@ void HomeView::onTabPopup(Workspace* workspace)
if (!menu) if (!menu)
return; return;
menu->showPopup(mousePosInDisplay()); menu->showPopup(mousePosInDisplay(), display());
} }
void HomeView::onWorkspaceViewSelected() void HomeView::onWorkspaceViewSelected()

View File

@ -40,7 +40,6 @@
#include "app/ui_context.h" #include "app/ui_context.h"
#include "base/fs.h" #include "base/fs.h"
#include "os/system.h" #include "os/system.h"
#include "os/window.h"
#include "ui/message.h" #include "ui/message.h"
#include "ui/splitter.h" #include "ui/splitter.h"
#include "ui/system.h" #include "ui/system.h"
@ -368,13 +367,12 @@ void MainWindow::onResize(ui::ResizeEvent& ev)
{ {
app::gen::MainWindow::onResize(ev); app::gen::MainWindow::onResize(ev);
gfx::Size desktopSize = ui::get_desktop_size();
ui::Display* display = this->display(); ui::Display* display = this->display();
if ((display) && if ((display) &&
(display->nativeWindow()->scale()*ui::guiscale() > 2) && (display->scale()*ui::guiscale() > 2) &&
(!m_scalePanic) && (!m_scalePanic) &&
(desktopSize.w/ui::guiscale() < 320 || (display->size().w / ui::guiscale() < 320 ||
desktopSize.h/ui::guiscale() < 260)) { display->size().h / ui::guiscale() < 260)) {
showNotification(m_scalePanic = new ScreenScalePanic); showNotification(m_scalePanic = new ScreenScalePanic);
} }
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -76,9 +76,11 @@ void Notifications::onClick(ui::Event& ev)
invalidate(); invalidate();
gfx::Rect bounds = this->bounds(); gfx::Rect bounds = this->bounds();
m_popup.showPopup(gfx::Point( m_popup.showPopup(
gfx::Point(
bounds.x - m_popup.sizeHint().w, bounds.x - m_popup.sizeHint().w,
bounds.y + bounds.h)); bounds.y2()),
display());
} }
} // namespace app } // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -22,6 +22,7 @@
#include "app/ui_context.h" #include "app/ui_context.h"
#include "ui/box.h" #include "ui/box.h"
#include "ui/button.h" #include "ui/button.h"
#include "ui/fit_bounds.h"
#include "ui/scale.h" #include "ui/scale.h"
#include "ui/theme.h" #include "ui/theme.h"
#include "ui/view.h" #include "ui/view.h"
@ -58,13 +59,21 @@ PalettePopup::PalettePopup()
initTheme(); initTheme();
} }
void PalettePopup::showPopup(const gfx::Rect& bounds) void PalettePopup::showPopup(ui::Display* display,
const gfx::Rect& buttonPos)
{ {
m_popup->loadPal()->setEnabled(false); m_popup->loadPal()->setEnabled(false);
m_popup->openFolder()->setEnabled(false); m_popup->openFolder()->setEnabled(false);
m_paletteListBox.selectChild(NULL); m_paletteListBox.selectChild(NULL);
moveWindow(bounds); fit_bounds(display, this,
gfx::Rect(buttonPos.x, buttonPos.y2(), 32, 32),
[](const gfx::Rect& workarea,
gfx::Rect& bounds,
std::function<gfx::Rect(Widget*)> getWidgetBounds) {
bounds.w = workarea.w/2;
bounds.h = workarea.h*3/4;
});
openWindowInForeground(); openWindowInForeground();
} }

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -26,7 +27,8 @@ namespace app {
public: public:
PalettePopup(); PalettePopup();
void showPopup(const gfx::Rect& bounds); void showPopup(ui::Display* display,
const gfx::Rect& buttonPos);
protected: protected:
void onPalChange(doc::Palette* palette); void onPalChange(doc::Palette* palette);

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -40,8 +40,8 @@ void PopupWindowPin::setPinned(const bool pinned)
if (m_pinned) if (m_pinned)
makeFloating(); makeFloating();
else { else {
gfx::Rect rc = bounds(); gfx::Rect rc = boundsOnScreen();
rc.enlarge(8); rc.enlarge(8 * guiscale());
setHotRegion(gfx::Region(rc)); setHotRegion(gfx::Region(rc));
makeFixed(); makeFixed();
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -33,6 +33,7 @@
#include "ui/base.h" #include "ui/base.h"
#include "ui/button.h" #include "ui/button.h"
#include "ui/close_event.h" #include "ui/close_event.h"
#include "ui/fit_bounds.h"
#include "ui/message.h" #include "ui/message.h"
#include "ui/system.h" #include "ui/system.h"
@ -219,25 +220,27 @@ bool PreviewEditorWindow::onProcessMessage(ui::Message* msg)
{ {
switch (msg->type()) { switch (msg->type()) {
case kOpenMessage: case kOpenMessage: {
{ Manager* manager = this->manager();
SkinTheme* theme = SkinTheme::instance(); Display* mainDisplay = manager->display();
// Default bounds gfx::Rect defaultBounds(mainDisplay->size() / 4);
gfx::Size desktopSize = ui::get_desktop_size(); SkinTheme* theme = SkinTheme::instance();
const int width = desktopSize.w/4; gfx::Rect mainWindow = manager->bounds();
const int height = desktopSize.h/4;
int extra = 2*theme->dimensions.miniScrollbarSize();
setBounds(
gfx::Rect(
desktopSize.w - width - ToolBar::instance()->bounds().w - extra,
desktopSize.h - height - StatusBar::instance()->bounds().h - extra,
width, height));
load_window_pos(this, "MiniEditor", false); int extra = theme->dimensions.miniScrollbarSize();
invalidate(); if (get_multiple_displays()) {
extra *= mainDisplay->scale();
} }
defaultBounds.x = mainWindow.x2() - ToolBar::instance()->sizeHint().w - defaultBounds.w - extra;
defaultBounds.y = mainWindow.y2() - StatusBar::instance()->sizeHint().h - defaultBounds.h - extra;
fit_bounds(mainDisplay, this, defaultBounds);
load_window_pos(this, "MiniEditor", false);
invalidate();
break; break;
}
case kCloseMessage: case kCloseMessage:
save_window_pos(this, "MiniEditor"); save_window_pos(this, "MiniEditor");

View File

@ -878,6 +878,8 @@ void StatusBar::showSnapToGridWarning(bool state)
if (!m_snapToGridWindow) if (!m_snapToGridWindow)
m_snapToGridWindow = new SnapToGridWindow; m_snapToGridWindow = new SnapToGridWindow;
m_snapToGridWindow->setDisplay(display(), false);
if (!m_snapToGridWindow->isVisible()) { if (!m_snapToGridWindow->isVisible()) {
m_snapToGridWindow->openWindow(); m_snapToGridWindow->openWindow();
m_snapToGridWindow->remapWindow(); m_snapToGridWindow->remapWindow();

View File

@ -1243,12 +1243,12 @@ bool Timeline::onProcessMessage(Message* msg)
if (!m_confPopup->isVisible()) { if (!m_confPopup->isVisible()) {
gfx::Rect bounds = m_confPopup->bounds(); gfx::Rect bounds = m_confPopup->bounds();
ui::fit_bounds(display(), BOTTOM, gearBounds, bounds); ui::fit_bounds(display(), BOTTOM, gearBounds, bounds);
ui::fit_bounds(display(), m_confPopup, bounds);
m_confPopup->moveWindow(bounds);
m_confPopup->openWindow(); m_confPopup->openWindow();
} }
else else {
m_confPopup->closeWindow(NULL); m_confPopup->closeWindow(nullptr);
}
break; break;
} }
@ -1258,7 +1258,7 @@ bool Timeline::onProcessMessage(Message* msg)
if (m_clk.frame == m_hot.frame) { if (m_clk.frame == m_hot.frame) {
Menu* popupMenu = AppMenus::instance()->getFramePopupMenu(); Menu* popupMenu = AppMenus::instance()->getFramePopupMenu();
if (popupMenu) { if (popupMenu) {
popupMenu->showPopup(mouseMsg->position()); popupMenu->showPopup(mouseMsg->position(), display());
m_state = STATE_STANDBY; m_state = STATE_STANDBY;
invalidate(); invalidate();
@ -1273,7 +1273,7 @@ bool Timeline::onProcessMessage(Message* msg)
if (m_clk.layer == m_hot.layer) { if (m_clk.layer == m_hot.layer) {
Menu* popupMenu = AppMenus::instance()->getLayerPopupMenu(); Menu* popupMenu = AppMenus::instance()->getLayerPopupMenu();
if (popupMenu) { if (popupMenu) {
popupMenu->showPopup(mouseMsg->position()); popupMenu->showPopup(mouseMsg->position(), display());
m_state = STATE_STANDBY; m_state = STATE_STANDBY;
invalidate(); invalidate();
@ -1293,7 +1293,7 @@ bool Timeline::onProcessMessage(Message* msg)
AppMenus::instance()->getCelMovementPopupMenu(): AppMenus::instance()->getCelMovementPopupMenu():
AppMenus::instance()->getCelPopupMenu(); AppMenus::instance()->getCelPopupMenu();
if (popupMenu) { if (popupMenu) {
popupMenu->showPopup(mouseMsg->position()); popupMenu->showPopup(mouseMsg->position(), display());
// Do not drop in this function, the drop is done from // Do not drop in this function, the drop is done from
// the menu in case we've used the // the menu in case we've used the
@ -1321,7 +1321,7 @@ bool Timeline::onProcessMessage(Message* msg)
Menu* popupMenu = AppMenus::instance()->getTagPopupMenu(); Menu* popupMenu = AppMenus::instance()->getTagPopupMenu();
if (popupMenu) { if (popupMenu) {
AppMenuItem::setContextParams(params); AppMenuItem::setContextParams(params);
popupMenu->showPopup(mouseMsg->position()); popupMenu->showPopup(mouseMsg->position(), display());
AppMenuItem::setContextParams(Params()); AppMenuItem::setContextParams(Params());
m_state = STATE_STANDBY; m_state = STATE_STANDBY;

View File

@ -376,7 +376,7 @@ bool ComboBox::onProcessMessage(Message* msg)
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg); MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
Widget* pick = manager()->pickFromScreenPos( Widget* pick = manager()->pickFromScreenPos(
display()->nativeWindow()->pointFromScreen(mouseMsg->position())); mouseMsg->display()->nativeWindow()->pointFromScreen(mouseMsg->position()));
if (pick && pick->hasAncestor(this)) if (pick && pick->hasAncestor(this))
return true; return true;
} }

View File

@ -33,6 +33,7 @@ namespace ui {
os::Window* nativeWindow() { return m_nativeWindow.get(); } os::Window* nativeWindow() { return m_nativeWindow.get(); }
os::Surface* surface() const; os::Surface* surface() const;
int scale() const { return m_nativeWindow->scale(); }
gfx::Size size() const; gfx::Size size() const;
gfx::Rect bounds() const { return gfx::Rect(size()); } gfx::Rect bounds() const { return gfx::Rect(size()); }

View File

@ -819,7 +819,7 @@ void Entry::showEditPopupMenu(const gfx::Point& pt)
paste.setEnabled(false); paste.setEnabled(false);
} }
menu.showPopup(pt); menu.showPopup(pt, display());
} }
class Entry::CalcBoxesTextDelegate : public os::DrawTextDelegate { class Entry::CalcBoxesTextDelegate : public os::DrawTextDelegate {

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // 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 // Copyright (C) 2001-2016 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -16,6 +16,7 @@
#include "ui/base.h" #include "ui/base.h"
#include "ui/display.h" #include "ui/display.h"
#include "ui/system.h" #include "ui/system.h"
#include "ui/window.h"
namespace ui { namespace ui {
@ -87,4 +88,50 @@ int fit_bounds(Display* display, int arrowAlign, const gfx::Rect& target, gfx::R
return arrowAlign; return arrowAlign;
} }
void fit_bounds(Display* parentDisplay,
Window* window,
const gfx::Rect& candidateBoundsRelativeToParentDisplay,
std::function<void(const gfx::Rect& workarea,
gfx::Rect& bounds,
std::function<gfx::Rect(Widget*)> getWidgetBounds)> fitLogic)
{
gfx::Point pos = candidateBoundsRelativeToParentDisplay.origin();
if (get_multiple_displays() && window->shouldCreateNativeWindow()) {
const os::Window* nativeWindow = parentDisplay->nativeWindow();
const gfx::Rect workarea = nativeWindow->screen()->workarea();
const int scale = nativeWindow->scale();
// Screen frame bounds
gfx::Rect frame(
nativeWindow->pointToScreen(pos),
candidateBoundsRelativeToParentDisplay.size() * scale);
if (fitLogic)
fitLogic(workarea, frame, [](Widget* widget){ return widget->boundsOnScreen(); });
frame.x = base::clamp(frame.x, workarea.x, workarea.x2() - frame.w);
frame.y = base::clamp(frame.y, workarea.y, workarea.y2() - frame.h);
// Set frame bounds directly
window->setBounds(gfx::Rect(0, 0, frame.w / scale, frame.h / scale));
window->loadNativeFrame(frame);
if (window->isVisible())
window->expandWindow(frame.size() / scale);
}
else {
const gfx::Rect displayBounds(parentDisplay->size());
gfx::Rect frame(candidateBoundsRelativeToParentDisplay);
if (fitLogic)
fitLogic(displayBounds, frame, [](Widget* widget){ return widget->bounds(); });
frame.x = base::clamp(frame.x, 0, displayBounds.w - frame.w);
frame.y = base::clamp(frame.y, 0, displayBounds.h - frame.h);
window->setBounds(frame);
}
}
} // namespace ui } // namespace ui

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2016 David Capello // Copyright (C) 2016 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -11,12 +11,32 @@
#include "gfx/fwd.h" #include "gfx/fwd.h"
#include <functional>
namespace ui { namespace ui {
class Display; class Display;
class Widget;
class Window;
int fit_bounds(Display* display, int arrowAlign, const gfx::Rect& target, gfx::Rect& bounds); int fit_bounds(Display* display, int arrowAlign, const gfx::Rect& target, gfx::Rect& bounds);
// Fits a possible rectangle for the given window fitting it with a
// special logic. It works for both cases:
// 1. With multiple windows (so the limit is the parentDisplay screen workarea)
// 2. Or with one window (so the limit is the just display area)
//
// The getWidgetBounds() can be used to get other widgets positions
// in the "fitLogic" (the bounds will be relative to the screen or
// to the display depending if get_multiple_displays() is true or
// false).
void fit_bounds(Display* parentDisplay,
Window* window,
const gfx::Rect& candidateBoundsRelativeToParentDisplay,
std::function<void(const gfx::Rect& workarea,
gfx::Rect& bounds,
std::function<gfx::Rect(Widget*)> getWidgetBounds)> fitLogic = nullptr);
} // namespace ui } // namespace ui
#endif #endif

View File

@ -16,6 +16,7 @@
#include "gfx/rect.h" #include "gfx/rect.h"
#include "gfx/region.h" #include "gfx/region.h"
#include "os/font.h" #include "os/font.h"
#include "ui/fit_bounds.h"
#include "ui/manager.h" #include "ui/manager.h"
#include "ui/message.h" #include "ui/message.h"
#include "ui/popup_window.h" #include "ui/popup_window.h"
@ -182,20 +183,28 @@ void IntEntry::openPopup()
m_popupWindow->addChild(&m_slider); m_popupWindow->addChild(&m_slider);
m_popupWindow->Close.connect(&IntEntry::onPopupClose, this); m_popupWindow->Close.connect(&IntEntry::onPopupClose, this);
Rect rc = bounds(); fit_bounds(
gfx::Size sz = m_popupWindow->sizeHint(); display(),
gfx::Size displaySize = display()->size(); m_popupWindow,
rc.w = 128*guiscale(); gfx::Rect(0, 0, 128*guiscale(), m_popupWindow->sizeHint().h),
if (rc.x+rc.w > displaySize.w) [this](const gfx::Rect& workarea,
rc.x = rc.x-rc.w+bounds().w; gfx::Rect& rc,
if (rc.y+rc.h+sz.h < displaySize.h) std::function<gfx::Rect(Widget*)> getWidgetBounds) {
rc.y += rc.h; Rect entryBounds = getWidgetBounds(this);
else
rc.y -= sz.h;
m_popupWindow->setBounds(rc);
Region rgn(rc.createUnion(bounds())); rc.x = entryBounds.x;
rgn.createUnion(rgn, Region(bounds())); rc.y = entryBounds.y2();
if (rc.x2() > workarea.x2())
rc.x = rc.x-rc.w+entryBounds.w;
if (rc.y2() > workarea.y2())
rc.y = entryBounds.y-entryBounds.h;
m_popupWindow->setBounds(rc);
});
Region rgn(m_popupWindow->boundsOnScreen().createUnion(boundsOnScreen()));
m_popupWindow->setHotRegion(rgn); m_popupWindow->setHotRegion(rgn);
m_popupWindow->openWindow(); m_popupWindow->openWindow();

View File

@ -24,6 +24,7 @@
#include "base/time.h" #include "base/time.h"
#include "os/event.h" #include "os/event.h"
#include "os/event_queue.h" #include "os/event_queue.h"
#include "os/screen.h"
#include "os/surface.h" #include "os/surface.h"
#include "os/system.h" #include "os/system.h"
#include "os/window.h" #include "os/window.h"
@ -1132,6 +1133,21 @@ void Manager::removeMessagesForTimer(Timer* timer)
} }
} }
void Manager::removeMessagesForDisplay(Display* display)
{
#ifdef DEBUG_UI_THREADS
ASSERT(manager_thread == base::this_thread::native_id());
#endif
for (Message* msg : msg_queue)
if (msg->display() == display)
msg->removeRecipient(msg->recipient());
for (Message* msg : used_msg_queue)
if (msg->display() == display)
msg->removeRecipient(msg->recipient());
}
void Manager::removePaintMessagesForDisplay(Display* display) void Manager::removePaintMessagesForDisplay(Display* display)
{ {
#ifdef DEBUG_UI_THREADS #ifdef DEBUG_UI_THREADS
@ -1141,7 +1157,7 @@ void Manager::removePaintMessagesForDisplay(Display* display)
for (auto it=msg_queue.begin(); it != msg_queue.end(); ) { for (auto it=msg_queue.begin(); it != msg_queue.end(); ) {
Message* msg = *it; Message* msg = *it;
if (msg->type() == kPaintMessage && if (msg->type() == kPaintMessage &&
static_cast<PaintMessage*>(msg)->display() == display) { msg->display() == display) {
delete msg; delete msg;
it = msg_queue.erase(it); it = msg_queue.erase(it);
} }
@ -1269,22 +1285,18 @@ void Manager::_openWindow(Window* window, bool center)
// Relayout // Relayout
if (center) if (center)
window->centerWindow(); window->centerWindow(parentDisplay);
else else
window->layout(); window->layout();
// If the window already was set a display, we don't setup it // If the window already was set a display, we don't setup it
// (i.e. in the case of combobox popup/window the display field is // (i.e. in the case of combobox popup/window the display field is
// set to the same display where the ComboBox widget is located) // set to the same display where the ComboBox widget is located)
if (window->display() == &m_display) { if (!window->hasDisplaySet()) {
// In other case, we can try to create a display/native window for // In other case, we can try to create a display/native window for
// the UI window. // the UI window.
if (get_multiple_displays() if (get_multiple_displays()
&& !window->isDesktop() && window->shouldCreateNativeWindow()) {
#if 1 // TODO Add support for menuboxes and tooltips with native windows
&& window->isSizeable()
#endif
) {
const int scale = parentDisplay->nativeWindow()->scale(); const int scale = parentDisplay->nativeWindow()->scale();
os::WindowSpec spec; os::WindowSpec spec;
@ -1298,6 +1310,31 @@ void Manager::_openWindow(Window* window, bool center)
frame *= scale; frame *= scale;
frame.offset(relativeToFrame.origin()); frame.offset(relativeToFrame.origin());
} }
// Limit window position using the union of all workareas
//
// TODO at least the title bar should be visible so we can
// resize it, because workareas can form an irregular shape
// (not rectangular) the calculation is a little more
// complex
{
gfx::Region wa;
os::ScreenList screens;
os::instance()->listScreens(screens);
for (const auto& screen : screens)
wa |= gfx::Region(screen->workarea());
// TODO use a "visibleFrameRegion = frame & wa" to check the
// visible regions and calculate if we should move the frame
// position
gfx::Rect waBounds = wa.bounds();
if (frame.x < waBounds.x) frame.x = waBounds.x;
if (frame.y < waBounds.y) frame.y = waBounds.y;
if (frame.x2() > waBounds.x2()) frame.w -= frame.x2() - waBounds.x2();
if (frame.y2() > waBounds.y2()) frame.h -= frame.y2() - waBounds.y2();
}
spec.position(os::WindowSpec::Position::Frame); spec.position(os::WindowSpec::Position::Frame);
spec.frame(frame); spec.frame(frame);
spec.scale(scale); spec.scale(scale);
@ -1374,8 +1411,8 @@ void Manager::_closeWindow(Window* window, bool redraw_background)
ASSERT(windowDisplay); ASSERT(windowDisplay);
ASSERT(windowDisplay != this->display()); ASSERT(windowDisplay != this->display());
// Remove all paint messages for this display. // Remove all messages for this display.
removePaintMessagesForDisplay(windowDisplay); removeMessagesForDisplay(windowDisplay);
window->setDisplay(nullptr, false); window->setDisplay(nullptr, false);
windowDisplay->nativeWindow()->setUserData<void*>(nullptr); windowDisplay->nativeWindow()->setUserData<void*>(nullptr);

View File

@ -80,6 +80,7 @@ namespace ui {
void removeMessagesFor(Widget* widget); void removeMessagesFor(Widget* widget);
void removeMessagesFor(Widget* widget, MessageType type); void removeMessagesFor(Widget* widget, MessageType type);
void removeMessagesForTimer(Timer* timer); void removeMessagesForTimer(Timer* timer);
void removeMessagesForDisplay(Display* display);
void removePaintMessagesForDisplay(Display* display); void removePaintMessagesForDisplay(Display* display);
void addMessageFilter(int message, Widget* widget); void addMessageFilter(int message, Widget* widget);

View File

@ -292,7 +292,8 @@ bool MenuItem::hasSubmenu() const
return (m_submenu && !m_submenu->children().empty()); return (m_submenu && !m_submenu->children().empty());
} }
void Menu::showPopup(const gfx::Point& pos) void Menu::showPopup(const gfx::Point& pos,
Display* parentDisplay)
{ {
// Generally, when we call showPopup() the menu shouldn't contain a // Generally, when we call showPopup() the menu shouldn't contain a
// parent menu-box, because we're filtering kMouseDownMessage to // parent menu-box, because we're filtering kMouseDownMessage to
@ -322,11 +323,9 @@ void Menu::showPopup(const gfx::Point& pos)
window->remapWindow(); window->remapWindow();
// Menubox position fit_bounds(parentDisplay,
gfx::Size displaySize = display()->size(); window.get(),
window->positionWindow( gfx::Rect(pos, window->size()));
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()); add_scrollbars_if_needed(window.get());
@ -442,20 +441,20 @@ bool MenuBox::onProcessMessage(Message* msg)
case kMouseDownMessage: case kMouseDownMessage:
case kDoubleClickMessage: case kDoubleClickMessage:
if (menu) { if (menu && msg->display()) {
ASSERT(menu->parent() == this); ASSERT(menu->parent() == this);
if (get_base(this)->is_processing) if (get_base(this)->is_processing)
break; break;
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position(); const gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
const gfx::Point screenPos = msg->display()->nativeWindow()->pointToScreen(mousePos);
// Here we catch the filtered messages (menu-bar or the // Here we catch the filtered messages (menu-bar or the
// popuped menu-box) to detect if the user press outside of // popuped menu-box) to detect if the user press outside of
// the widget // the widget
if (msg->type() == kMouseDownMessage && m_base != nullptr) { if (msg->type() == kMouseDownMessage && m_base != nullptr) {
Widget* picked = manager()->pickFromScreenPos( Widget* picked = manager()->pickFromScreenPos(screenPos);
display()->nativeWindow()->pointToScreen(mousePos));
// If one of these conditions are accomplished we have to // If one of these conditions are accomplished we have to
// close all menus (back to menu-bar or close the popuped // close all menus (back to menu-bar or close the popuped
@ -466,16 +465,17 @@ bool MenuBox::onProcessMessage(Message* msg)
(get_base_menubox(picked) != this || (get_base_menubox(picked) != this ||
(this->type() == kMenuBarWidget && (this->type() == kMenuBarWidget &&
picked->type() == kMenuWidget))) { picked->type() == kMenuWidget))) {
// The user click outside all the menu-box/menu-items, close all // The user click outside all the menu-box/menu-items, close all
menu->closeAll(); menu->closeAll();
// Change to "return false" if you want to send the click
// to the window after closing all menus.
return true; return true;
} }
} }
// Get the widget below the mouse cursor // Get the widget below the mouse cursor
Widget* picked = menu->pick(mousePos); if (Widget* picked = menu->pickFromScreenPos(screenPos)) {
if (picked) {
if ((picked->type() == kMenuItemWidget) && if ((picked->type() == kMenuItemWidget) &&
!(picked->hasFlags(DISABLED))) { !(picked->hasFlags(DISABLED))) {
MenuItem* pickedItem = static_cast<MenuItem*>(picked); MenuItem* pickedItem = static_cast<MenuItem*>(picked);
@ -814,9 +814,6 @@ bool MenuItem::onProcessMessage(Message* msg)
ASSERT(base->is_processing); ASSERT(base->is_processing);
ASSERT(hasSubmenu()); ASSERT(hasSubmenu());
Rect old_pos = window()->bounds();
old_pos.w -= 1*guiscale();
MenuBox* menubox = new MenuBox(); MenuBox* menubox = new MenuBox();
m_submenu_menubox = menubox; m_submenu_menubox = menubox;
menubox->setMenu(m_submenu); menubox->setMenu(m_submenu);
@ -824,42 +821,47 @@ bool MenuItem::onProcessMessage(Message* msg)
// New window and new menu-box // New window and new menu-box
auto window = new MenuBoxWindow(menubox); auto window = new MenuBoxWindow(menubox);
// Menubox position fit_bounds(
Rect pos = window->bounds(); display(), window, window->bounds(),
Size displaySize = display()->size(); [this](const gfx::Rect& workarea,
gfx::Rect& pos,
std::function<gfx::Rect(Widget*)> getWidgetBounds){
Rect parentPos = getWidgetBounds(this->window());
Rect bounds = getWidgetBounds(this);
if (inBar()) { if (inBar()) {
pos.x = base::clamp(bounds().x, 0, displaySize.w-pos.w); pos.x = base::clamp(bounds.x, workarea.x, workarea.x2()-pos.w);
pos.y = std::max(0, bounds().y2()); pos.y = std::max(workarea.y, bounds.y2());
} }
else { else {
int x_left = old_pos.x - pos.w; int x_left = parentPos.x - pos.w + 1*guiscale();
int x_right = old_pos.x2(); int x_right = parentPos.x2() - 1*guiscale();
int x, y = bounds().y-3*guiscale(); int x, y = bounds.y-3*guiscale();
Rect r1(0, 0, pos.w, pos.h), r2(0, 0, pos.w, pos.h); Rect r1(0, 0, pos.w, pos.h);
Rect r2(0, 0, pos.w, pos.h);
r1.x = x_left = base::clamp(x_left, 0, displaySize.w-pos.w); r1.x = x_left = base::clamp(x_left, 0, workarea.w-pos.w);
r2.x = x_right = base::clamp(x_right, 0, displaySize.w-pos.w); r2.x = x_right = base::clamp(x_right, 0, workarea.w-pos.w);
r1.y = r2.y = y = base::clamp(y, 0, displaySize.h-pos.h); r1.y = r2.y = y = base::clamp(y, 0, workarea.h-pos.h);
// Calculate both intersections // Calculate both intersections
gfx::Rect s1 = r1.createIntersection(old_pos); const gfx::Rect s1 = r1.createIntersection(parentPos);
gfx::Rect s2 = r2.createIntersection(old_pos); const gfx::Rect s2 = r2.createIntersection(parentPos);
if (s2.isEmpty()) if (s2.isEmpty())
x = x_right; // Use the right because there aren't intersection with it x = x_right; // Use the right because there aren't intersection with it
else if (s1.isEmpty()) else if (s1.isEmpty())
x = x_left; // Use the left because there are not intersection x = x_left; // Use the left because there are not intersection
else if (r2.w*r2.h <= r1.w*r1.h) else if (s2.w*s2.h <= s1.w*s1.h)
x = x_right; // Use the right because there are less intersection area x = x_right; // Use the right because there are less intersection area
else else
x = x_left; // Use the left because there are less intersection area x = x_left; // Use the left because there are less intersection area
pos.x = x; pos.x = x;
pos.y = y; pos.y = y;
} }
});
window->positionWindow(pos.x, pos.y);
add_scrollbars_if_needed(window); add_scrollbars_if_needed(window);
// Set the focus to the new menubox // Set the focus to the new menubox
@ -1133,7 +1135,7 @@ void Menu::unhighlightItem()
highlightItem(nullptr, false, false, false); highlightItem(nullptr, false, false, false);
} }
bool MenuItem::inBar() bool MenuItem::inBar() const
{ {
return return
(parent() && (parent() &&

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -28,7 +28,8 @@ namespace ui {
Menu(); Menu();
~Menu(); ~Menu();
void showPopup(const gfx::Point& pos); void showPopup(const gfx::Point& pos,
Display* parentDisplay);
Widget* findItemById(const char* id); Widget* findItemById(const char* id);
// Returns the MenuItem that has as submenu this menu. // Returns the MenuItem that has as submenu this menu.
@ -141,7 +142,7 @@ namespace ui {
virtual void onClick(); virtual void onClick();
virtual void onValidate(); virtual void onValidate();
bool inBar(); bool inBar() const;
private: private:
void openSubmenu(bool select_first); void openSubmenu(bool select_first);

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -27,8 +27,6 @@ PopupWindow::PopupWindow(const std::string& text,
: Window(text.empty() ? WithoutTitleBar: WithTitleBar, text) : Window(text.empty() ? WithoutTitleBar: WithTitleBar, text)
, m_clickBehavior(clickBehavior) , m_clickBehavior(clickBehavior)
, m_enterBehavior(enterBehavior) , m_enterBehavior(enterBehavior)
, m_filtering(false)
, m_fixed(false)
{ {
setSizeable(false); setSizeable(false);
setMoveable(false); setMoveable(false);
@ -53,11 +51,11 @@ PopupWindow::~PopupWindow()
stopFilteringMessages(); stopFilteringMessages();
} }
void PopupWindow::setHotRegion(const gfx::Region& region) void PopupWindow::setHotRegion(const gfx::Region& screenRegion)
{ {
startFilteringMessages(); startFilteringMessages();
m_hotRegion = region; m_hotRegion = screenRegion;
} }
void PopupWindow::setClickBehavior(ClickBehavior behavior) void PopupWindow::setClickBehavior(ClickBehavior behavior)
@ -137,26 +135,30 @@ bool PopupWindow::onProcessMessage(Message* msg)
case kMouseDownMessage: case kMouseDownMessage:
if (m_filtering && if (m_filtering &&
manager()->getTopWindow() == this) { manager()->getTopWindow() == this &&
msg->display()) {
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position(); gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
gfx::Point screenPos = msg->display()->nativeWindow()->pointToScreen(mousePos);
switch (m_clickBehavior) { switch (m_clickBehavior) {
// If the user click outside the window, we have to close // If the user click outside the window, we have to close
// the tooltip window. // the tooltip window.
case ClickBehavior::CloseOnClickInOtherWindow: { case ClickBehavior::CloseOnClickInOtherWindow: {
Widget* picked = pick(mousePos); Widget* picked = pickFromScreenPos(screenPos);
if (!picked || picked->window() != this) { if (!picked || picked->window() != this) {
closeWindow(nullptr); closeWindow(nullptr);
} }
break; break;
} }
case ClickBehavior::CloseOnClickOutsideHotRegion: case ClickBehavior::CloseOnClickOutsideHotRegion: {
if (!m_hotRegion.contains(mousePos)) { // Convert the mousePos from display() coordinates to screen
if (!m_hotRegion.contains(screenPos)) {
closeWindow(nullptr); closeWindow(nullptr);
} }
break; break;
}
} }
} }
break; break;
@ -164,12 +166,14 @@ bool PopupWindow::onProcessMessage(Message* msg)
case kMouseMoveMessage: case kMouseMoveMessage:
if (m_fixed && if (m_fixed &&
!m_hotRegion.isEmpty() && !m_hotRegion.isEmpty() &&
manager()->getCapture() == nullptr) { manager()->getCapture() == nullptr &&
msg->display()) {
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position(); gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
gfx::Point screenPos = msg->display()->nativeWindow()->pointToScreen(mousePos);
// If the mouse is outside the hot-region we have to close the // If the mouse is outside the hot-region we have to close the
// window. // window.
if (!m_hotRegion.contains(mousePos)) if (!m_hotRegion.contains(screenPos))
closeWindow(nullptr); closeWindow(nullptr);
} }
break; break;

View File

@ -1,4 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -33,7 +34,10 @@ namespace ui {
// Sets the hot region. This region indicates the area where the // Sets the hot region. This region indicates the area where the
// mouse can be located and the window will be kept open. // mouse can be located and the window will be kept open.
void setHotRegion(const gfx::Region& region); //
// The screenRegion must be specified in native screen coordinates.
void setHotRegion(const gfx::Region& screenRegion);
void setClickBehavior(ClickBehavior behavior); void setClickBehavior(ClickBehavior behavior);
void setEnterBehavior(EnterBehavior behavior); void setEnterBehavior(EnterBehavior behavior);
@ -54,8 +58,8 @@ namespace ui {
ClickBehavior m_clickBehavior; ClickBehavior m_clickBehavior;
EnterBehavior m_enterBehavior; EnterBehavior m_enterBehavior;
gfx::Region m_hotRegion; gfx::Region m_hotRegion;
bool m_filtering; bool m_filtering = false;
bool m_fixed; bool m_fixed = false;
}; };
class TransparentPopupWindow : public PopupWindow { class TransparentPopupWindow : public PopupWindow {

View File

@ -698,6 +698,16 @@ Rect Widget::clientChildrenBounds() const
m_bounds.h - border().height()); m_bounds.h - border().height());
} }
gfx::Rect Widget::boundsOnScreen() const
{
gfx::Rect rc = bounds();
os::Window* nativeWindow = display()->nativeWindow();
rc = gfx::Rect(
nativeWindow->pointToScreen(rc.origin()),
nativeWindow->pointToScreen(rc.point2()));
return rc;
}
void Widget::setBounds(const Rect& rc) void Widget::setBounds(const Rect& rc)
{ {
ResizeEvent ev(this, rc); ResizeEvent ev(this, rc);

View File

@ -242,6 +242,9 @@ namespace ui {
gfx::Rect childrenBounds() const; gfx::Rect childrenBounds() const;
gfx::Rect clientChildrenBounds() const; gfx::Rect clientChildrenBounds() const;
// Bounds of this widget or window on native screen/desktop coordinates.
gfx::Rect boundsOnScreen() const;
// Sets the bounds of the widget generating a onResize() event. // Sets the bounds of the widget generating a onResize() event.
void setBounds(const gfx::Rect& rc); void setBounds(const gfx::Rect& rc);

View File

@ -309,19 +309,24 @@ void Window::remapWindow()
invalidate(); invalidate();
} }
void Window::centerWindow() void Window::centerWindow(Display* parentDisplay)
{ {
Widget* manager = this->manager();
if (m_isAutoRemap) if (m_isAutoRemap)
remapWindow(); remapWindow();
positionWindow(manager->bounds().w/2 - bounds().w/2, if (!parentDisplay)
manager->bounds().h/2 - bounds().h/2); parentDisplay = manager()->getDefault()->display();
gfx::Size displaySize = parentDisplay->size();
positionWindow(displaySize.w/2 - bounds().w/2,
displaySize.h/2 - bounds().h/2);
} }
void Window::positionWindow(int x, int y) void Window::positionWindow(int x, int y)
{ {
// TODO don't use in ownDisplay() windows
ASSERT(!ownDisplay());
if (m_isAutoRemap) if (m_isAutoRemap)
remapWindow(); remapWindow();
@ -437,6 +442,12 @@ bool Window::onProcessMessage(Message* msg)
} }
if (action != os::WindowAction::Cancel) { if (action != os::WindowAction::Cancel) {
display()->nativeWindow()->performWindowAction(action, nullptr); display()->nativeWindow()->performWindowAction(action, nullptr);
// As Window::moveWindow() will not be called, we have to
// call onWindowMovement() event from here.
if (action == os::WindowAction::Move)
onWindowMovement();
return true; return true;
} }
} }

View File

@ -31,6 +31,7 @@ namespace ui {
bool ownDisplay() const { return m_ownDisplay; } bool ownDisplay() const { return m_ownDisplay; }
Display* display() const; Display* display() const;
void setDisplay(Display* display, const bool own); void setDisplay(Display* display, const bool own);
bool hasDisplaySet() const { return m_display != nullptr; }
Widget* closer() const { return m_closer; } Widget* closer() const { return m_closer; }
@ -41,7 +42,7 @@ namespace ui {
void setWantFocus(bool state); void setWantFocus(bool state);
void remapWindow(); void remapWindow();
void centerWindow(); void centerWindow(Display* parentDisplay = nullptr);
void positionWindow(int x, int y); void positionWindow(int x, int y);
void moveWindow(const gfx::Rect& rect); void moveWindow(const gfx::Rect& rect);
@ -60,6 +61,11 @@ namespace ui {
bool isSizeable() const { return m_isSizeable; } bool isSizeable() const { return m_isSizeable; }
bool isMoveable() const { return m_isMoveable; } bool isMoveable() const { return m_isMoveable; }
bool shouldCreateNativeWindow() const {
return (!isDesktop() &&
!isTransparent());
}
HitTest hitTest(const gfx::Point& point); HitTest hitTest(const gfx::Point& point);
// Last native window frame bounds. Saved just before we close the // Last native window frame bounds. Saved just before we close the