[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-usability
Subject:    Re: RFC: Restricting mouse in popup menus
From:       Lubos Lunak <l.lunak () suse ! cz>
Date:       2005-05-18 14:52:20
Message-ID: 200505181652.21090.l.lunak () suse ! cz
[Download RAW message or body]

On Tuesday 17 of May 2005 21:59, Diego Moya (a.k.a. TuringTest) wrote:
> On 17/05/05, Lubos Lunak <l.lunak@suse.cz> wrote:
> > 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.
>
> I would allow it but only as long as the mouse button is pressed, thus
> turning it into a quasimode.
> ( http://www.answers.com/main/ntquery?s=quasimode ).

 Hmm, the definition means nothing to me other than being a definition.

 As for keeping the mouse pressed, the intention is to make navigation in the 
menus easier. I don't think having to keep a mouse button pressed helps that, 
rather the other way around.

>
> >  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 ;).
>
> The reason it feels odd is because it breaks one of the main
> established dogmas of GUIs, that of the mouse always moving freely no
> matter the state of the system. A really big consistency break. Not
> good.

 I didn't know there was such a dogma. Actually the reason why it felt odd for 
me was that the computer was moving the mouse instead of me (which I'm sure 
is also described among some dogmas as a no-no).

>
> In general, restricting the movement of the mouse to a section of the
> screen is a bad idea, because it forbids the user to change her mind
> an beginning a different task. In particular, it would make impossible
> to cancel the menu without making a selection.

 That's why there's the area at the top where clicking cancels the menu.

> Changing the cursor image or hiding it while the quasimode is active
> would solve this problem by clearly changing the perception of the
> user on what's happening.

 Sounds like a very good idea ... *checking* ... hmm, now there's one more 
#define NO_CURSOR. Either there's no cursor at all, so there's no longer 
"restricted" mouse movement, but perhaps it's too strange to have no cursor 
at all, or there's a different arrow shape.

 Are the attached patches better?

-- 
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-04-11 13:09:46.000000000 +0200
+++ qapplication_x11.cpp	2005-05-18 16:16:34.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"
@@ -3847,8 +3848,50 @@ bool qt_try_modal( QWidget *widget, XEve
 	    QWidget *widget	The popup widget to be removed
  *****************************************************************************/
 
+#define CHANGE_ACCEL
+#define NO_CURSOR
+//#define RESET_MOUSE_POS
+#if defined(NO_CURSOR) && !defined(RESET_MOUSE_POS)
+#define RESET_MOUSE_POS
+#endif
+#include <X11/cursorfont.h>
+
+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++;
@@ -3859,22 +3902,44 @@ 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)) ) {
+#ifndef NO_CURSOR
+            static Cursor cur = XCreateFontCursor( popup->x11Display(), XC_arrow );
+#endif
 	    r = XGrabPointer( popup->x11Display(), popup->winId(), TRUE,
 			      (uint)(ButtonPressMask | ButtonReleaseMask |
 				     ButtonMotionMask | EnterWindowMask |
 				     LeaveWindowMask | PointerMotionMask),
 			      GrabModeSync, GrabModeAsync,
-			      None, None, CurrentTime );
+#ifdef NO_CURSOR
+			      popupRegionWindow, blankCursor.handle(), CurrentTime );
+#else
+			      popupRegionWindow, cur, CurrentTime );
+#endif
 
 	    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 );
     }
 
@@ -3902,6 +3967,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) ) )
@@ -3913,6 +3981,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 ) {
@@ -3935,21 +4006,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-03-30 17:50:32.000000000 +0200
+++ qpopupmenu.cpp	2005-05-17 14:37:31.000000000 +0200
@@ -854,6 +854,7 @@ bool QPopupMenu::tryMenuBar( QMouseEvent
 */
 bool QPopupMenu::tryMouseEvent( QPopupMenu *p, QMouseEvent * e)
 {
+    return false;
     if ( p == this )
 	return FALSE;
     QPoint pos = mapFromGlobal( e->globalPos() );
@@ -1602,7 +1603,7 @@ void QPopupMenu::mousePressEvent( QMouse
     int item = itemAtPos( e->pos() );
     if ( item == -1 ) {
 	if ( !rect().contains(e->pos()) && !tryMenuBar(e) ) {
-	    byeMenuBar();
+	    hide();
 	}
 	return;
     }
@@ -1637,6 +1638,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
@@ -1652,10 +1656,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() ) {
@@ -1761,14 +1771,14 @@ 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
         d->hasmouse = 1;
-	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 );
 
@@ -1787,14 +1797,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