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:
David Capello 2016-11-19 00:57:58 -03:00
parent f054aaa2cc
commit 3f1f07807b
5 changed files with 196 additions and 160 deletions

View File

@ -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()???

View File

@ -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);

View File

@ -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();

View File

@ -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
};

View File

@ -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;