[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-usability
Subject: RFC: Restricting mouse in popup menus
From: Lubos Lunak <l.lunak () suse ! cz>
Date: 2005-05-17 12:43:15
Message-ID: 200505171443.15667.l.lunak () suse ! cz
[Download RAW message or body]
Hello,
I'd like to get some feedback on the attached Qt patches :). The idea is
based on a feature from the window manager called WindowLab (ggl:windowlab).
In short, when a popup menu is open, the mouse movement is restricted only to
the area of the popup - this allows navigating in the menus only using
vertical movement and clicking.
In not so short: When a popup is opened, the mouse area is restricted to its
geometry plus 2 pixels above. The mouse pointer is initially positioned in
that area above the popup, and clicking there closes it. Submenus don't open
automatically, they always have to be clicked. The patch also significantly
increases the mouse acceleration threshold, so that the mouse is less
sensitive in the popup.
This should have some advantages:
- The already mentioned vertical-only movement. Since the mouse is restricted
to the popup geometry, you cannot move it out by going far to the left or
right. Opening a submenu restricts the y-position to the submenu's geometry,
closing the submenu moves it back. It's not possible to accidentally open
another submenu while trying to move the mouse to the right to a submenu.
- Since the starting position is always at the topleft corner above the popup,
one cannot accidentally click the bottommost Quit/Close/Whatever entry as
sometimes happens with popups opened close to the bottom screen edge. This
also means the navigation in the popup is always the same.
The main disadvantage that I've found out so far is that it feels a bit odd,
at least for the first five minutes, but since it seems to fit well together
with the standalone menubar feature, which is also KDE-only, I guess this
should not be a big problem ;).
There are two #define's in the code that select whether to change the mouse
acceleration threshold (CHANGE_ACCEL) and whether to restore the mouse
position after closing a popup (RESET_MOUSE_POS). I'm not sure if they're
better turned on or off.
If people like this, I intend to clean up the patches and make it an optional
KDE feature.
--
Lubos Lunak
KDE developer
---------------------------------------------------------------------
SuSE CR, s.r.o. e-mail: l.lunak@suse.cz , l.lunak@kde.org
Drahobejlova 27 tel: +420 2 9654 2373
190 00 Praha 9 fax: +420 2 9654 2374
Czech Republic http://www.suse.cz/
["qapplication_x11.cpp.patch" (text/x-diff)]
--- qapplication_x11.cpp.sav 2005-05-05 13:45:18.000000000 +0200
+++ qapplication_x11.cpp 2005-05-17 13:30:25.000000000 +0200
@@ -87,6 +87,7 @@
#include "qsettings.h"
#include "qstylefactory.h"
#include "qfileinfo.h"
+#include "qpopupmenu.h"
// Input method stuff - UNFINISHED
#include "qinputcontext_p.h"
@@ -3846,8 +3847,45 @@ bool qt_try_modal( QWidget *widget, XEve
QWidget *widget The popup widget to be removed
*****************************************************************************/
+#define RESET_MOUSE_POS
+#define CHANGE_ACCEL
+
+static Window popupRegionWindow = None;
+static QValueList< QPoint >* popupPointerPositions;
+static void popupApplyRegion( QWidget* popup, bool set )
+{
+ QRect r( 0, 0, XDisplayWidth( popup->x11Display(), popup->x11Screen()),
+ XDisplayHeight( popup->x11Display(), popup->x11Screen()));
+ QPopupMenu* p = qt_cast< QPopupMenu* >( popup );
+ if( p ) {
+ r = p->contentsRect();
+ r.moveBy( p->x(), p->y());
+ r.addCoords( 0, -2, 0, 0 );
+ if( set ) {
+ if( !popupPointerPositions ) {
+ popupPointerPositions = new QValueList< QPoint >;
+ Q_CHECK_PTR( popupPointerPositions );
+ }
+ popupPointerPositions->append( QCursor::pos());
+ }
+ }
+ XMoveResizeWindow( popup->x11Display(), popupRegionWindow,
+ r.x(), r.y(), r.width(), r.height());
+ if( p ) {
+ if( set ) {
+ XWarpPointer( popup->x11Display(), None, popupRegionWindow, 0, 0, 0, 0, 0, 0 );
+ } else {
+ QPoint p = popupPointerPositions->back();
+ popupPointerPositions->pop_back();
+#ifdef RESET_MOUSE_POS
+ QCursor::setPos( p );
+#endif
+ }
+ }
+}
static int openPopupCount = 0;
+static int accelthresh;
void QApplication::openPopup( QWidget *popup )
{
openPopupCount++;
@@ -3858,6 +3896,15 @@ void QApplication::openPopup( QWidget *p
popupWidgets->append( popup ); // add to end of list
if ( popupWidgets->count() == 1 && !qt_nograb() ){ // grab mouse/keyboard
+ XSetWindowAttributes attrs;
+ attrs.override_redirect = True;
+ popupRegionWindow = XCreateWindow( popup->x11Display(),
+ RootWindow( popup->x11Display(), popup->x11Screen()),
+ 0, 0, 1, 1, 0, 0,
+ InputOnly, CopyFromParent, CWOverrideRedirect, &attrs );
+ popupApplyRegion( popup, true );
+ XLowerWindow( popup->x11Display(), popupRegionWindow );
+ XMapWindow( popup->x11Display(), popupRegionWindow );
int r = XGrabKeyboard( popup->x11Display(), popup->winId(), FALSE,
GrabModeSync, GrabModeAsync, CurrentTime );
if ( (popupGrabOk = (r == GrabSuccess)) ) {
@@ -3866,14 +3913,20 @@ void QApplication::openPopup( QWidget *p
ButtonMotionMask | EnterWindowMask |
LeaveWindowMask | PointerMotionMask),
GrabModeSync, GrabModeAsync,
- None, None, CurrentTime );
+ popupRegionWindow, None, CurrentTime );
if ( (popupGrabOk = (r == GrabSuccess)) )
XAllowEvents( popup->x11Display(), SyncPointer, CurrentTime );
else
XUngrabKeyboard( popup->x11Display(), CurrentTime );
- }
+#ifdef CHANGE_ACCEL
+ int dummy;
+ XGetPointerControl( popup->x11Display(), &dummy, &dummy, &accelthresh );
+ XChangePointerControl( popup->x11Display(), False, True, 1, 1, 200 );
+#endif
+ }
} else if ( popupGrabOk ) {
+ popupApplyRegion( popup, true );
XAllowEvents( popup->x11Display(), SyncPointer, CurrentTime );
}
@@ -3901,6 +3954,9 @@ void QApplication::closePopup( QWidget *
popupCloseDownMode = TRUE; // control mouse events
delete popupWidgets;
popupWidgets = 0;
+#ifdef CHANGE_ACCEL
+ XChangePointerControl( popup->x11Display(), False, True, 1, 1, accelthresh );
+#endif
if ( !qt_nograb() && popupGrabOk ) { // grabbing not disabled
if ( mouseButtonState != 0
|| popup->geometry(). contains(QPoint(mouseGlobalXPos, mouseGlobalYPos) ) )
@@ -3912,6 +3968,9 @@ void QApplication::closePopup( QWidget *
XAllowEvents( popup->x11Display(), ReplayPointer,CurrentTime );
}
XUngrabPointer( popup->x11Display(), CurrentTime );
+ popupApplyRegion( popup, false );
+ XDestroyWindow( popup->x11Display(), popupRegionWindow );
+ popupRegionWindow = None;
XFlush( popup->x11Display() );
}
if ( active_window ) {
@@ -3934,21 +3993,9 @@ void QApplication::closePopup( QWidget *
else
aw->setFocus();
QFocusEvent::resetReason();
- if ( popupWidgets->count() == 1 && !qt_nograb() ){ // grab mouse/keyboard
- int r = XGrabKeyboard( aw->x11Display(), aw->winId(), FALSE,
- GrabModeSync, GrabModeAsync, CurrentTime );
- if ( (popupGrabOk = (r == GrabSuccess)) ) {
- r = XGrabPointer( aw->x11Display(), aw->winId(), TRUE,
- (uint)(ButtonPressMask | ButtonReleaseMask |
- ButtonMotionMask | EnterWindowMask |
- LeaveWindowMask | PointerMotionMask),
- GrabModeSync, GrabModeAsync,
- None, None, CurrentTime );
-
- if ( (popupGrabOk = (r == GrabSuccess)) )
- XAllowEvents( aw->x11Display(), SyncPointer, CurrentTime );
- }
- }
+ if ( popupGrabOk ) {
+ popupApplyRegion( aw, false );
+ }
}
}
["qpopupmenu.cpp.patch" (text/x-diff)]
--- qpopupmenu.cpp.sav 2005-05-05 13:46:05.000000000 +0200
+++ qpopupmenu.cpp 2005-05-17 13:30:43.000000000 +0200
@@ -851,6 +851,7 @@ bool QPopupMenu::tryMenuBar( QMouseEvent
*/
bool QPopupMenu::tryMouseEvent( QPopupMenu *p, QMouseEvent * e)
{
+ return false;
if ( p == this )
return FALSE;
QPoint pos = mapFromGlobal( e->globalPos() );
@@ -1598,7 +1599,7 @@ void QPopupMenu::mousePressEvent( QMouse
int item = itemAtPos( e->pos() );
if ( item == -1 ) {
if ( !rect().contains(e->pos()) && !tryMenuBar(e) ) {
- byeMenuBar();
+ hide();
}
return;
}
@@ -1633,6 +1634,9 @@ void QPopupMenu::mouseReleaseEvent( QMou
if ( !parentMenu && !mouseBtDn && actItem < 0 && motion < 6 )
return;
+ if( !mouseBtDn )
+ return;
+
mouseBtDn = FALSE;
// if the user released the mouse outside the menu, pass control
@@ -1648,10 +1652,16 @@ void QPopupMenu::mouseReleaseEvent( QMou
if ( actItem < 0 ) { // we do not have an active item
// if the release is inside without motion (happens with
// oversized popup menus on small screens), ignore it
- if ( rect().contains( e->pos() ) && motion < 6 )
- return;
- else
- byeMenuBar();
+// if ( rect().contains( e->pos() ) && motion < 6 )
+// return;
+// else
+// hide();
+ QMenuData* p = parentMenu;
+ hide();
+#ifndef QT_NO_MENUBAR
+ if ( p && p->isMenuBar )
+ ((QMenuBar*) p)->goodbye( TRUE );
+#endif
} else { // selected menu item!
register QMenuItem *mi = mitems->at(actItem);
if ( mi ->widget() ) {
@@ -1747,13 +1757,13 @@ void QPopupMenu::mouseMoveEvent( QMouseE
updateRow( lastActItem );
if ( lastActItem > 0 ||
( !rect().contains( e->pos() ) && !tryMenuBar( e ) ) ) {
- popupSubMenuLater(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay,
- this), this);
+// popupSubMenuLater(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay,
+// this), this);
}
} else { // mouse on valid item
// but did not register mouse press
- if ( (e->state() & Qt::MouseButtonMask) && !mouseBtDn )
- mouseBtDn = TRUE; // so mouseReleaseEvent will pop down
+// if ( (e->state() & Qt::MouseButtonMask) && !mouseBtDn )
+// mouseBtDn = TRUE; // so mouseReleaseEvent will pop down
register QMenuItem *mi = mitems->at( item );
@@ -1772,14 +1782,14 @@ void QPopupMenu::mouseMoveEvent( QMouseE
if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this) &&
d->mouseMoveBuffer.contains( e->pos() ) ) {
actItem = item;
- popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this) * 6,
- this );
+// popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this) * 6,
+// this );
return;
}
if ( mi->popup() || ( popupActive >= 0 && popupActive != item ))
- popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this),
- this );
+;// popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this),
+// this );
else if ( singleSingleShot )
singleSingleShot->stop();
_______________________________________________
kde-usability mailing list
kde-usability@kde.org
https://mail.kde.org/mailman/listinfo/kde-usability
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic