mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-02 11:59:58 +00:00
Process os::Event::MouseEnter/Leave in correct order
When we received a MouseEnter/Leave, we were processing those laf-os events immediately without taking care of all the MouseMove/Down/Up events in the middle. Now we enqueue all the events as messages (MouseEnter -> MouseMove/Down/Up -> MouseLeave). This is important because when we process MouseLeave we are calling setMouse(nullptr), which resets the mouse widget HAS_MOUSE flag, and some widgets needs to know if they have the mouse above before the MouseUp event. With this change we can remove the Widget::hasMouseOver() function (that was checking the global ui::get_mouse_position() instead of the HAS_MOUSE flag directly). Another change was introduced to keep the old behavior now that hasMouseOver() is not available: the ui::Manager doesn't assign the HAS_MOUSE flag if the mouse is captured (it only assign the HAS_MOUSE to the widget with the mouse captured, or to no widget, at least until the capture is released).
This commit is contained in:
parent
48275d51c2
commit
bd91a6430f
@ -277,7 +277,7 @@ void ColorButton::onPaint(PaintEvent& ev)
|
||||
draw_color_button(g, rc,
|
||||
color,
|
||||
(doc::ColorMode)m_pixelFormat,
|
||||
hasMouseOver(), false);
|
||||
hasMouse(), false);
|
||||
|
||||
// Draw text
|
||||
std::string str = m_color.toHumanReadableString(m_pixelFormat,
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2022 Igara Studio S.A.
|
||||
// Copyright (C) 2022-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -56,7 +56,7 @@ void IconButton::onPaint(PaintEvent& ev)
|
||||
fg = theme->colors.menuitemHighlightText();
|
||||
bg = theme->colors.menuitemHighlightFace();
|
||||
}
|
||||
else if (isEnabled() && hasMouseOver()) {
|
||||
else if (isEnabled() && hasMouse()) {
|
||||
fg = theme->colors.menuitemHotText();
|
||||
bg = theme->colors.menuitemHotFace();
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ void Notifications::onPaint(PaintEvent& ev)
|
||||
Graphics* g = ev.graphics();
|
||||
|
||||
PaintWidgetPartInfo info;
|
||||
if (hasMouseOver()) info.styleFlags |= ui::Style::Layer::kMouse;
|
||||
if (hasMouse()) info.styleFlags |= ui::Style::Layer::kMouse;
|
||||
if (m_red) info.styleFlags |= ui::Style::Layer::kFocus;
|
||||
if (isSelected()) info.styleFlags |= ui::Style::Layer::kSelected;
|
||||
|
||||
|
@ -1489,16 +1489,16 @@ void SkinTheme::paintSlider(PaintEvent& ev)
|
||||
SkinPartPtr empty_part;
|
||||
|
||||
if (isMiniLook) {
|
||||
full_part = widget->hasMouseOver() ? parts.miniSliderFullFocused():
|
||||
parts.miniSliderFull();
|
||||
empty_part = widget->hasMouseOver() ? parts.miniSliderEmptyFocused():
|
||||
parts.miniSliderEmpty();
|
||||
full_part = (widget->hasMouse() ? parts.miniSliderFullFocused():
|
||||
parts.miniSliderFull());
|
||||
empty_part = (widget->hasMouse() ? parts.miniSliderEmptyFocused():
|
||||
parts.miniSliderEmpty());
|
||||
}
|
||||
else {
|
||||
full_part = widget->hasFocus() ? parts.sliderFullFocused():
|
||||
parts.sliderFull();
|
||||
empty_part = widget->hasFocus() ? parts.sliderEmptyFocused():
|
||||
parts.sliderEmpty();
|
||||
full_part = (widget->hasFocus() ? parts.sliderFullFocused():
|
||||
parts.sliderFull());
|
||||
empty_part = (widget->hasFocus() ? parts.sliderEmptyFocused():
|
||||
parts.sliderEmpty());
|
||||
}
|
||||
|
||||
if (value == min)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -1042,7 +1042,7 @@ void Tabs::updateDragTabIndexes(int mouseX, bool startAni)
|
||||
startAni = true;
|
||||
}
|
||||
}
|
||||
else if (hasMouseOver()) {
|
||||
else if (hasMouse()) {
|
||||
i = std::clamp(i, 0, int(m_list.size())-1);
|
||||
if (i != m_dragTabIndex) {
|
||||
m_list.erase(m_list.begin()+m_dragTabIndex);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2020-2022 Igara Studio S.A.
|
||||
// Copyright (c) 2020-2023 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -164,7 +164,7 @@ void TileButton::onPaint(PaintEvent& ev)
|
||||
Site site = UIContext::instance()->activeSite();
|
||||
draw_tile_button(g, rc,
|
||||
site, m_tile,
|
||||
hasMouseOver(), false);
|
||||
hasMouse(), false);
|
||||
|
||||
// Draw text
|
||||
if (m_tile != doc::notile) {
|
||||
|
@ -3241,7 +3241,7 @@ void Timeline::updateScrollBars()
|
||||
void Timeline::updateByMousePos(ui::Message* msg, const gfx::Point& mousePos)
|
||||
{
|
||||
Hit hit = hitTest(msg, mousePos);
|
||||
if (hasMouseOver())
|
||||
if (hasMouse())
|
||||
setCursor(msg, hit);
|
||||
setHot(hit);
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ bool ButtonBase::onProcessMessage(Message* msg)
|
||||
if (hasCapture()) {
|
||||
releaseMouse();
|
||||
|
||||
if (hasMouseOver()) {
|
||||
if (hasMouse()) {
|
||||
switch (m_behaviorType) {
|
||||
|
||||
case kButtonWidget:
|
||||
@ -270,7 +270,7 @@ void ButtonBase::onStartDrag()
|
||||
|
||||
void ButtonBase::onSelectWhenDragging()
|
||||
{
|
||||
bool hasMouse = hasMouseOver();
|
||||
const bool hasMouse = this->hasMouse();
|
||||
|
||||
// Switch state when the mouse go out
|
||||
if ((hasMouse && isSelected() != m_pressedStatus) ||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2018 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -48,7 +48,7 @@ bool LinkLabel::onProcessMessage(Message* msg)
|
||||
|
||||
case kSetCursorMessage:
|
||||
// TODO theme stuff
|
||||
if (isEnabled() && hasMouseOver()) {
|
||||
if (isEnabled() && hasMouse()) {
|
||||
set_mouse_cursor(kHandCursor);
|
||||
return true;
|
||||
}
|
||||
@ -66,7 +66,7 @@ bool LinkLabel::onProcessMessage(Message* msg)
|
||||
|
||||
case kMouseMoveMessage:
|
||||
if (isEnabled() && hasCapture())
|
||||
setSelected(hasMouseOver());
|
||||
setSelected(hasMouse());
|
||||
break;
|
||||
|
||||
case kMouseDownMessage:
|
||||
@ -83,7 +83,7 @@ bool LinkLabel::onProcessMessage(Message* msg)
|
||||
setSelected(false);
|
||||
invalidate(); // TODO theme specific
|
||||
|
||||
if (hasMouseOver())
|
||||
if (hasMouse())
|
||||
onClick();
|
||||
}
|
||||
break;
|
||||
|
@ -496,30 +496,37 @@ void Manager::generateMessagesFromOSEvents()
|
||||
}
|
||||
|
||||
case os::Event::MouseEnter: {
|
||||
if (get_multiple_displays()) {
|
||||
if (osEvent.window()) {
|
||||
ASSERT(display != nullptr);
|
||||
_internal_set_mouse_display(display);
|
||||
auto msg = new CallbackMessage([osEvent, display]{
|
||||
if (get_multiple_displays()) {
|
||||
if (osEvent.window()) {
|
||||
ASSERT(display != nullptr);
|
||||
_internal_set_mouse_display(display);
|
||||
}
|
||||
}
|
||||
}
|
||||
set_mouse_cursor(kArrowCursor);
|
||||
set_mouse_cursor(kArrowCursor);
|
||||
mouse_display = display;
|
||||
});
|
||||
enqueueMessage(msg);
|
||||
lastMouseMoveEvent = osEvent;
|
||||
mouse_display = display;
|
||||
break;
|
||||
}
|
||||
|
||||
case os::Event::MouseLeave: {
|
||||
if (mouse_display == display) {
|
||||
set_mouse_cursor(kOutsideDisplay);
|
||||
setMouse(nullptr);
|
||||
auto msg = new CallbackMessage([this, display]{
|
||||
if (mouse_display == display) {
|
||||
set_mouse_cursor(kOutsideDisplay);
|
||||
setMouse(nullptr);
|
||||
|
||||
_internal_no_mouse_position();
|
||||
mouse_display = nullptr;
|
||||
_internal_no_mouse_position();
|
||||
mouse_display = nullptr;
|
||||
}
|
||||
});
|
||||
|
||||
// To avoid calling kSetCursorMessage when the mouse leaves
|
||||
// the window.
|
||||
lastMouseMoveEvent = os::Event();
|
||||
}
|
||||
enqueueMessage(msg);
|
||||
|
||||
// To avoid calling kSetCursorMessage when the mouse leaves
|
||||
// the window.
|
||||
lastMouseMoveEvent = os::Event();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1000,54 +1007,61 @@ void Manager::setMouse(Widget* widget)
|
||||
(widget ? widget->id(): ""));
|
||||
#endif
|
||||
|
||||
if ((mouse_widget != widget) && (!capture_widget)) {
|
||||
Widget* commonAncestor = findLowestCommonAncestor(mouse_widget, widget);
|
||||
if (mouse_widget == widget)
|
||||
return;
|
||||
|
||||
// Fetch the mouse
|
||||
if (mouse_widget && mouse_widget != commonAncestor) {
|
||||
auto msg = new Message(kMouseLeaveMessage);
|
||||
msg->setRecipient(mouse_widget);
|
||||
msg->setPropagateToParent(true);
|
||||
msg->setCommonAncestor(commonAncestor);
|
||||
enqueueMessage(msg);
|
||||
Widget* commonAncestor = findLowestCommonAncestor(mouse_widget, widget);
|
||||
|
||||
// Remove HAS_MOUSE from all the hierarchy
|
||||
auto a = mouse_widget;
|
||||
while (a && a != commonAncestor) {
|
||||
a->disableFlags(HAS_MOUSE);
|
||||
a = a->parent();
|
||||
}
|
||||
// Fetch the mouse
|
||||
if (mouse_widget && mouse_widget != commonAncestor) {
|
||||
auto msg = new Message(kMouseLeaveMessage);
|
||||
msg->setRecipient(mouse_widget);
|
||||
msg->setPropagateToParent(true);
|
||||
msg->setCommonAncestor(commonAncestor);
|
||||
enqueueMessage(msg);
|
||||
|
||||
// Remove HAS_MOUSE from all the hierarchy
|
||||
auto a = mouse_widget;
|
||||
while (a && a != commonAncestor) {
|
||||
a->disableFlags(HAS_MOUSE);
|
||||
a = a->parent();
|
||||
}
|
||||
}
|
||||
|
||||
// Put the mouse
|
||||
mouse_widget = widget;
|
||||
if (widget) {
|
||||
Display* display = mouse_widget->display();
|
||||
gfx::Point mousePos = display->nativeWindow()->pointFromScreen(get_mouse_position());
|
||||
// If the mouse is captured, we can just put the HAS_MOUSE flag in
|
||||
// the captured widget (or in none).
|
||||
if (capture_widget && capture_widget != widget) {
|
||||
widget = nullptr;
|
||||
}
|
||||
|
||||
auto msg = newMouseMessage(
|
||||
kMouseEnterMessage,
|
||||
display, nullptr,
|
||||
mousePos,
|
||||
PointerType::Unknown,
|
||||
m_mouseButton,
|
||||
kKeyUninitializedModifier);
|
||||
// Put the mouse
|
||||
mouse_widget = widget;
|
||||
if (widget) {
|
||||
Display* display = mouse_widget->display();
|
||||
gfx::Point mousePos = display->nativeWindow()->pointFromScreen(get_mouse_position());
|
||||
|
||||
msg->setRecipient(widget);
|
||||
msg->setPropagateToParent(true);
|
||||
msg->setCommonAncestor(commonAncestor);
|
||||
enqueueMessage(msg);
|
||||
generateSetCursorMessage(display,
|
||||
mousePos,
|
||||
kKeyUninitializedModifier,
|
||||
PointerType::Unknown);
|
||||
auto msg = newMouseMessage(
|
||||
kMouseEnterMessage,
|
||||
display, nullptr,
|
||||
mousePos,
|
||||
PointerType::Unknown,
|
||||
m_mouseButton,
|
||||
kKeyUninitializedModifier);
|
||||
|
||||
// Add HAS_MOUSE to all the hierarchy
|
||||
auto a = mouse_widget;
|
||||
while (a && a != commonAncestor) {
|
||||
a->enableFlags(HAS_MOUSE);
|
||||
a = a->parent();
|
||||
}
|
||||
msg->setRecipient(widget);
|
||||
msg->setPropagateToParent(true);
|
||||
msg->setCommonAncestor(commonAncestor);
|
||||
enqueueMessage(msg);
|
||||
generateSetCursorMessage(display,
|
||||
mousePos,
|
||||
kKeyUninitializedModifier,
|
||||
PointerType::Unknown);
|
||||
|
||||
// Add HAS_MOUSE to all the hierarchy
|
||||
auto a = mouse_widget;
|
||||
while (a && a != commonAncestor) {
|
||||
a->enableFlags(HAS_MOUSE);
|
||||
a = a->parent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1929,9 +1943,10 @@ bool Manager::sendMessageToWidget(Message* msg, Widget* widget)
|
||||
"kSetCursorMessage",
|
||||
"kMouseWheelMessage",
|
||||
"kTouchMagnifyMessage",
|
||||
"kCallbackMessage",
|
||||
};
|
||||
static_assert(kOpenMessage == 0 &&
|
||||
kTouchMagnifyMessage == sizeof(msg_name)/sizeof(const char*)-1,
|
||||
kCallbackMessage == sizeof(msg_name)/sizeof(const char*)-1,
|
||||
"MessageType enum has changed");
|
||||
const char* string =
|
||||
(msg->type() >= 0 &&
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -18,6 +18,8 @@
|
||||
#include "ui/mouse_button.h"
|
||||
#include "ui/pointer_type.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class Display;
|
||||
@ -81,6 +83,18 @@ namespace ui {
|
||||
KeyModifiers m_modifiers; // Key modifiers pressed when message was created
|
||||
};
|
||||
|
||||
class CallbackMessage : public Message {
|
||||
public:
|
||||
CallbackMessage(std::function<void()>&& callback)
|
||||
: Message(kCallbackMessage)
|
||||
, m_callback(std::move(callback)) { }
|
||||
void call() {
|
||||
m_callback();
|
||||
}
|
||||
private:
|
||||
std::function<void()> m_callback;
|
||||
};
|
||||
|
||||
class KeyMessage : public Message {
|
||||
public:
|
||||
KeyMessage(MessageType type,
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -45,6 +45,11 @@ namespace ui {
|
||||
// TODO Drag'n'drop messages...
|
||||
// k...DndMessage
|
||||
|
||||
// Call a generic function when we are processing the queue of
|
||||
// messages. Used to process an laf-os event in the same order
|
||||
// they were received in some cases (e.g. mouse leave/enter).
|
||||
kCallbackMessage,
|
||||
|
||||
// User widgets.
|
||||
kFirstRegisteredMessage,
|
||||
kLastRegisteredMessage = 0x7fffffff
|
||||
|
@ -1496,11 +1496,6 @@ bool Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::hasMouseOver() const
|
||||
{
|
||||
return (this == pickFromScreenPos(get_mouse_position()));
|
||||
}
|
||||
|
||||
gfx::Point Widget::mousePosInDisplay() const
|
||||
{
|
||||
return display()->nativeWindow()->pointFromScreen(get_mouse_position());
|
||||
|
@ -349,12 +349,29 @@ namespace ui {
|
||||
void captureMouse();
|
||||
void releaseMouse();
|
||||
|
||||
// True when the widget has the keyboard focus (only widgets with
|
||||
// FOCUS_STOP flag will receive the HAS_FOCUS flag/receive the
|
||||
// focus when the user press the tab key to navigate widgets).
|
||||
bool hasFocus() const { return hasFlags(HAS_FOCUS); }
|
||||
bool hasMouse() const { return hasFlags(HAS_MOUSE); }
|
||||
bool hasCapture() const { return hasFlags(HAS_CAPTURE); }
|
||||
|
||||
// Checking if the mouse is currently above the widget.
|
||||
bool hasMouseOver() const;
|
||||
// True when the widget has the mouse above. If the mouse leaves
|
||||
// the widget, the widget will lose the HAS_MOUSE flag. If some
|
||||
// widget captures the mouse, no other widget will have this flag,
|
||||
// so in this case there are just two options:
|
||||
//
|
||||
// 1) The widget with the capture (hasCapture()) will has the
|
||||
// mouse flag too.
|
||||
// 2) Or no other widget will have the mouse flag until the widget
|
||||
// releases the capture (releaseCapture())
|
||||
bool hasMouse() const { return hasFlags(HAS_MOUSE); }
|
||||
|
||||
// True when the widget has captured the mouse, e.g. generally
|
||||
// when the user press a mouse button above a clickeable widget
|
||||
// (e.g. ui::Button), the widget will capture the mouse
|
||||
// temporarily until the mouse button is released. If a widget
|
||||
// captures the mouse, it will receive all mouse events until it
|
||||
// release (even if the mouse moves outside the widget).
|
||||
bool hasCapture() const { return hasFlags(HAS_CAPTURE); }
|
||||
|
||||
// Returns the mouse position relative to the top-left corner of
|
||||
// the ui::Display's client area/content rect.
|
||||
|
Loading…
Reference in New Issue
Block a user