mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 10:20:48 +00:00
Add support to cycle RGB/HSV text fields with tab key (fix #1019)
Added a new flag in messages to know if they came from a filter or from the original chain of recipients. To do this, we've added a new way to process message filters: instead of pre-adding filters to the list of message recipients, we process filters in the Manager::pumpQueue() member function itself. (So the list of "recipients" is not modified.)
This commit is contained in:
parent
f054aaa2cc
commit
3f1f07807b
@ -47,8 +47,7 @@ namespace ui {
|
||||
|
||||
static const int NFILTERS = (int)(kFirstRegisteredMessage+1);
|
||||
|
||||
struct Filter
|
||||
{
|
||||
struct Filter {
|
||||
int message;
|
||||
Widget* widget;
|
||||
|
||||
@ -67,6 +66,7 @@ static WidgetsList new_windows; // Windows that we should show
|
||||
static WidgetsList mouse_widgets_list; // List of widgets to send mouse events
|
||||
static Messages msg_queue; // Messages queue
|
||||
static Filters msg_filters[NFILTERS]; // Filters for every enqueued message
|
||||
static int filter_locks = 0;
|
||||
|
||||
static Widget* focus_widget; // The widget with the focus
|
||||
static Widget* mouse_widget; // The widget with the mouse
|
||||
@ -83,6 +83,37 @@ static int cmp_right(Widget* widget, int x, int y);
|
||||
static int cmp_up(Widget* widget, int x, int y);
|
||||
static int cmp_down(Widget* widget, int x, int y);
|
||||
|
||||
namespace {
|
||||
|
||||
class LockFilters {
|
||||
public:
|
||||
LockFilters() {
|
||||
++filter_locks;
|
||||
}
|
||||
~LockFilters() {
|
||||
ASSERT(filter_locks > 0);
|
||||
--filter_locks;
|
||||
|
||||
if (filter_locks == 0) {
|
||||
// Clear empty filters
|
||||
for (Filters& msg_filter : msg_filters) {
|
||||
for (auto it = msg_filter.begin(); it != msg_filter.end(); ) {
|
||||
Filter* filter = *it;
|
||||
if (filter->widget == nullptr) {
|
||||
delete filter;
|
||||
it = msg_filter.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
Manager::Manager()
|
||||
: Widget(kManagerWidget)
|
||||
, m_display(NULL)
|
||||
@ -127,15 +158,13 @@ Manager::~Manager()
|
||||
Timer::checkNoTimers();
|
||||
|
||||
// Destroy filters
|
||||
for (int c=0; c<NFILTERS; ++c) {
|
||||
for (Filters::iterator it=msg_filters[c].begin(), end=msg_filters[c].end();
|
||||
it != end; ++it)
|
||||
delete *it;
|
||||
msg_filters[c].clear();
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
for (Filters& msg_filter : msg_filters)
|
||||
ASSERT(msg_filter.empty());
|
||||
#endif
|
||||
|
||||
// No more default manager
|
||||
m_defaultManager = NULL;
|
||||
m_defaultManager = nullptr;
|
||||
|
||||
// Shutdown system
|
||||
ASSERT(msg_queue.empty());
|
||||
@ -592,48 +621,10 @@ void Manager::addToGarbage(Widget* widget)
|
||||
m_garbage.push_back(widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param msg You can't use the this message after calling this
|
||||
* routine. The message will be automatically freed through
|
||||
* @ref jmessage_free
|
||||
*/
|
||||
void Manager::enqueueMessage(Message* msg)
|
||||
{
|
||||
ASSERT(msg != NULL);
|
||||
|
||||
#ifdef REPORT_EVENTS
|
||||
if (msg->type() == kKeyDownMessage ||
|
||||
msg->type() == kKeyUpMessage) {
|
||||
int mods = (int)static_cast<KeyMessage*>(msg)->modifiers();
|
||||
TRACE("Key%s scancode=%d unicode=%d mods=%s%s%s\n",
|
||||
(msg->type() == kKeyDownMessage ? "Down": "Up"),
|
||||
static_cast<KeyMessage*>(msg)->scancode(),
|
||||
static_cast<KeyMessage*>(msg)->unicodeChar(),
|
||||
mods & kKeyShiftModifier ? " Shift": "",
|
||||
mods & kKeyCtrlModifier ? " Ctrl": "",
|
||||
mods & kKeyAltModifier ? " Alt": "");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if this message must be filtered by some widget before
|
||||
int c = msg->type();
|
||||
if (c >= kFirstRegisteredMessage)
|
||||
c = kFirstRegisteredMessage;
|
||||
|
||||
if (!msg_filters[c].empty()) { // OK, so are filters to add...
|
||||
// Add all the filters in the destination list of the message
|
||||
for (Filters::reverse_iterator it=msg_filters[c].rbegin(),
|
||||
end=msg_filters[c].rend(); it != end; ++it) {
|
||||
Filter* filter = *it;
|
||||
if (msg->type() == filter->message)
|
||||
msg->prependRecipient(filter->widget);
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->hasRecipients())
|
||||
msg_queue.push_back(msg);
|
||||
else
|
||||
delete msg;
|
||||
ASSERT(msg);
|
||||
msg_queue.push_back(msg);
|
||||
}
|
||||
|
||||
Window* Manager::getTopWindow()
|
||||
@ -931,6 +922,7 @@ void Manager::removeMessagesForTimer(Timer* timer)
|
||||
|
||||
void Manager::addMessageFilter(int message, Widget* widget)
|
||||
{
|
||||
LockFilters lock;
|
||||
int c = message;
|
||||
if (c >= kFirstRegisteredMessage)
|
||||
c = kFirstRegisteredMessage;
|
||||
@ -940,32 +932,25 @@ void Manager::addMessageFilter(int message, Widget* widget)
|
||||
|
||||
void Manager::removeMessageFilter(int message, Widget* widget)
|
||||
{
|
||||
LockFilters lock;
|
||||
int c = message;
|
||||
if (c >= kFirstRegisteredMessage)
|
||||
c = kFirstRegisteredMessage;
|
||||
|
||||
for (Filters::iterator it=msg_filters[c].begin(); it != msg_filters[c].end(); ) {
|
||||
Filter* filter = *it;
|
||||
if (filter->widget == widget) {
|
||||
delete filter;
|
||||
it = msg_filters[c].erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
Filters& msg_filter = msg_filters[c];
|
||||
for (Filter* filter : msg_filter) {
|
||||
if (filter->widget == widget)
|
||||
filter->widget = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::removeMessageFilterFor(Widget* widget)
|
||||
{
|
||||
for (int c=0; c<NFILTERS; ++c) {
|
||||
for (Filters::iterator it=msg_filters[c].begin(); it != msg_filters[c].end(); ) {
|
||||
Filter* filter = *it;
|
||||
if (filter->widget == widget) {
|
||||
delete filter;
|
||||
it = msg_filters[c].erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
LockFilters lock;
|
||||
for (Filters& msg_filter : msg_filters) {
|
||||
for (Filter* filter : msg_filter) {
|
||||
if (filter->widget == widget)
|
||||
filter->widget = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1235,7 +1220,7 @@ void Manager::pumpQueue()
|
||||
base::tick_t t = base::current_tick();
|
||||
#endif
|
||||
|
||||
Messages::iterator it = msg_queue.begin();
|
||||
auto it = msg_queue.begin();
|
||||
while (it != msg_queue.end()) {
|
||||
#ifdef LIMIT_DISPATCH_TIME
|
||||
if (base::current_tick()-t > 250)
|
||||
@ -1262,102 +1247,36 @@ void Manager::pumpQueue()
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
for (auto widget : msg->recipients()) {
|
||||
if (!widget)
|
||||
continue;
|
||||
|
||||
#ifdef REPORT_EVENTS
|
||||
{
|
||||
static const char* msg_name[] = {
|
||||
"kOpenMessage",
|
||||
"kCloseMessage",
|
||||
"kCloseDisplayMessage",
|
||||
"kResizeDisplayMessage",
|
||||
"kPaintMessage",
|
||||
"kTimerMessage",
|
||||
"kDropFilesMessage",
|
||||
"kWinMoveMessage",
|
||||
// Send this message to filters
|
||||
{
|
||||
Filters& msg_filter = msg_filters[MIN(msg->type(), kFirstRegisteredMessage)];
|
||||
if (!msg_filter.empty()) {
|
||||
LockFilters lock;
|
||||
for (Filter* filter : msg_filter) {
|
||||
// The widget can be nullptr in case that the filter was
|
||||
// "pre-removed" (it'll finally erased from the
|
||||
// msg_filter list from ~LockFilters()).
|
||||
if (filter->widget != nullptr &&
|
||||
msg->type() == filter->message) {
|
||||
msg->setFromFilter(true);
|
||||
done = sendMessageToWidget(msg, filter->widget);
|
||||
msg->setFromFilter(false);
|
||||
|
||||
"kKeyDownMessage",
|
||||
"kKeyUpMessage",
|
||||
"kFocusEnterMessage",
|
||||
"kFocusLeaveMessage",
|
||||
|
||||
"kMouseDownMessage",
|
||||
"kMouseUpMessage",
|
||||
"kDoubleClickMessage",
|
||||
"kMouseEnterMessage",
|
||||
"kMouseLeaveMessage",
|
||||
"kMouseMoveMessage",
|
||||
"kSetCursorMessage",
|
||||
"kMouseWheelMessage",
|
||||
"kTouchMagnifyMessage",
|
||||
};
|
||||
const char* string =
|
||||
(msg->type() >= kOpenMessage &&
|
||||
msg->type() <= kMouseWheelMessage) ? msg_name[msg->type()]:
|
||||
"Unknown";
|
||||
|
||||
std::cout << "Event " << msg->type() << " (" << string << ") "
|
||||
<< "for " << typeid(*widget).name();
|
||||
if (!widget->id().empty())
|
||||
std::cout << " (" << widget->id() << ")";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
// We need to configure the clip region for paint messages
|
||||
// before we call Widget::sendMessage().
|
||||
if (msg->type() == kPaintMessage) {
|
||||
if (widget->hasFlags(HIDDEN))
|
||||
continue;
|
||||
|
||||
PaintMessage* paintMsg = static_cast<PaintMessage*>(msg);
|
||||
she::Surface* surface = m_display->getSurface();
|
||||
gfx::Rect oldClip = surface->getClipBounds();
|
||||
|
||||
if (surface->intersectClipRect(paintMsg->rect())) {
|
||||
#ifdef REPORT_EVENTS
|
||||
std::cout << " - clip("
|
||||
<< paintMsg->rect().x << ", "
|
||||
<< paintMsg->rect().y << ", "
|
||||
<< paintMsg->rect().w << ", "
|
||||
<< paintMsg->rect().h << ")"
|
||||
<< std::endl;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_PAINT_EVENTS
|
||||
{
|
||||
she::SurfaceLock lock(surface);
|
||||
surface->fillRect(gfx::rgba(0, 0, 255), paintMsg->rect());
|
||||
}
|
||||
|
||||
if (m_display)
|
||||
m_display->flip(gfx::Rect(0, 0, display_w(), display_h()));
|
||||
|
||||
base::this_thread::sleep_for(0.002);
|
||||
#endif
|
||||
|
||||
if (surface) {
|
||||
// Call the message handler
|
||||
done = widget->sendMessage(msg);
|
||||
|
||||
// Restore clip region for paint messages.
|
||||
surface->setClipBounds(oldClip);
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// As this kPaintMessage's rectangle was updated, we can
|
||||
// remove it from "m_invalidRegion".
|
||||
m_invalidRegion -= gfx::Region(paintMsg->rect());
|
||||
}
|
||||
else {
|
||||
// Call the message handler
|
||||
done = widget->sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (done)
|
||||
break;
|
||||
if (!done) {
|
||||
// Then send the message to its recipients
|
||||
for (Widget* widget : msg->recipients()) {
|
||||
done = sendMessageToWidget(msg, widget);
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the message from the msg_queue
|
||||
@ -1368,6 +1287,106 @@ void Manager::pumpQueue()
|
||||
}
|
||||
}
|
||||
|
||||
bool Manager::sendMessageToWidget(Message* msg, Widget* widget)
|
||||
{
|
||||
if (!widget)
|
||||
return false;
|
||||
|
||||
#ifdef REPORT_EVENTS
|
||||
{
|
||||
static const char* msg_name[] = {
|
||||
"kOpenMessage",
|
||||
"kCloseMessage",
|
||||
"kCloseDisplayMessage",
|
||||
"kResizeDisplayMessage",
|
||||
"kPaintMessage",
|
||||
"kTimerMessage",
|
||||
"kDropFilesMessage",
|
||||
"kWinMoveMessage",
|
||||
|
||||
"kKeyDownMessage",
|
||||
"kKeyUpMessage",
|
||||
"kFocusEnterMessage",
|
||||
"kFocusLeaveMessage",
|
||||
|
||||
"kMouseDownMessage",
|
||||
"kMouseUpMessage",
|
||||
"kDoubleClickMessage",
|
||||
"kMouseEnterMessage",
|
||||
"kMouseLeaveMessage",
|
||||
"kMouseMoveMessage",
|
||||
"kSetCursorMessage",
|
||||
"kMouseWheelMessage",
|
||||
"kTouchMagnifyMessage",
|
||||
};
|
||||
const char* string =
|
||||
(msg->type() >= kOpenMessage &&
|
||||
msg->type() <= kMouseWheelMessage) ? msg_name[msg->type()]:
|
||||
"Unknown";
|
||||
|
||||
std::cout << "Event " << msg->type() << " (" << string << ") "
|
||||
<< "for " << typeid(*widget).name();
|
||||
if (!widget->id().empty())
|
||||
std::cout << " (" << widget->id() << ")";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool used = false;
|
||||
|
||||
// We need to configure the clip region for paint messages
|
||||
// before we call Widget::sendMessage().
|
||||
if (msg->type() == kPaintMessage) {
|
||||
if (widget->hasFlags(HIDDEN))
|
||||
return false;
|
||||
|
||||
PaintMessage* paintMsg = static_cast<PaintMessage*>(msg);
|
||||
she::Surface* surface = m_display->getSurface();
|
||||
gfx::Rect oldClip = surface->getClipBounds();
|
||||
|
||||
if (surface->intersectClipRect(paintMsg->rect())) {
|
||||
#ifdef REPORT_EVENTS
|
||||
std::cout << " - clip("
|
||||
<< paintMsg->rect().x << ", "
|
||||
<< paintMsg->rect().y << ", "
|
||||
<< paintMsg->rect().w << ", "
|
||||
<< paintMsg->rect().h << ")"
|
||||
<< std::endl;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_PAINT_EVENTS
|
||||
{
|
||||
she::SurfaceLock lock(surface);
|
||||
surface->fillRect(gfx::rgba(0, 0, 255), paintMsg->rect());
|
||||
}
|
||||
|
||||
if (m_display)
|
||||
m_display->flip(gfx::Rect(0, 0, display_w(), display_h()));
|
||||
|
||||
base::this_thread::sleep_for(0.002);
|
||||
#endif
|
||||
|
||||
if (surface) {
|
||||
// Call the message handler
|
||||
used = widget->sendMessage(msg);
|
||||
|
||||
// Restore clip region for paint messages.
|
||||
surface->setClipBounds(oldClip);
|
||||
}
|
||||
}
|
||||
|
||||
// As this kPaintMessage's rectangle was updated, we can
|
||||
// remove it from "m_invalidRegion".
|
||||
m_invalidRegion -= gfx::Region(paintMsg->rect());
|
||||
}
|
||||
else {
|
||||
// Call the message handler
|
||||
used = widget->sendMessage(msg);
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
void Manager::invalidateDisplayRegion(const gfx::Region& region)
|
||||
{
|
||||
// TODO intersect with getDrawableRegion()???
|
||||
|
@ -45,11 +45,15 @@ namespace ui {
|
||||
// Refreshes the real display with the UI content.
|
||||
void flipDisplay();
|
||||
|
||||
// Adds the given "msg" message to the queue of messages to be
|
||||
// dispached. "msg" cannot be used after this function, it'll be
|
||||
// automatically deleted.
|
||||
void enqueueMessage(Message* msg);
|
||||
|
||||
// Returns true if there are messages in the queue to be
|
||||
// distpatched through jmanager_dispatch_messages().
|
||||
// dispatched through dispatchMessages().
|
||||
bool generateMessages();
|
||||
void dispatchMessages();
|
||||
void enqueueMessage(Message* msg);
|
||||
|
||||
void addToGarbage(Widget* widget);
|
||||
void collectGarbage();
|
||||
@ -146,6 +150,8 @@ namespace ui {
|
||||
void handleWindowZOrder();
|
||||
|
||||
void pumpQueue();
|
||||
bool sendMessageToWidget(Message* msg, Widget* widget);
|
||||
|
||||
static void removeWidgetFromRecipients(Widget* widget, Message* msg);
|
||||
static bool someParentIsFocusStop(Widget* widget);
|
||||
static Widget* findMagneticWidget(Widget* widget);
|
||||
|
@ -22,6 +22,7 @@ namespace ui {
|
||||
Message::Message(MessageType type, KeyModifiers modifiers)
|
||||
: m_type(type)
|
||||
, m_used(false)
|
||||
, m_fromFilter(false)
|
||||
{
|
||||
if (modifiers == kKeyUninitializedModifier)
|
||||
m_modifiers = she::instance()->keyModifiers();
|
||||
|
@ -37,6 +37,8 @@ namespace ui {
|
||||
const WidgetsList& recipients() const { return m_recipients; }
|
||||
bool hasRecipients() const { return !m_recipients.empty(); }
|
||||
bool isUsed() const { return m_used; }
|
||||
bool fromFilter() const { return m_fromFilter; }
|
||||
void setFromFilter(bool state) { m_fromFilter = state; }
|
||||
void markAsUsed() { m_used = true; }
|
||||
KeyModifiers modifiers() const { return m_modifiers; }
|
||||
bool shiftPressed() const { return (m_modifiers & kKeyShiftModifier) == kKeyShiftModifier; }
|
||||
@ -57,9 +59,10 @@ namespace ui {
|
||||
void broadcastToChildren(Widget* widget);
|
||||
|
||||
private:
|
||||
MessageType m_type; // Type of message
|
||||
MessageType m_type; // Type of message
|
||||
WidgetsList m_recipients; // List of recipients of the message
|
||||
bool m_used; // Was used
|
||||
bool m_fromFilter; // Sent from pre-filter
|
||||
KeyModifiers m_modifiers; // Key modifiers pressed when message was created
|
||||
};
|
||||
|
||||
|
@ -116,6 +116,13 @@ bool PopupWindow::onProcessMessage(Message* msg)
|
||||
closeWindow(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the message came from a filter, we don't send it back to
|
||||
// the default Window processing (which will send the message
|
||||
// to the Manager). In this way, the focused children can
|
||||
// process the kKeyDownMessage.
|
||||
if (msg->fromFilter())
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user