More cleanups to the UI.

Context menus, both as right-mouse button popups and those part of
PopupWidgets now have arrows that support scrolling, without having to
continuously click the mouse button.  The scroll arrow color also
changes to disabled when they reach the upper or lower range.
Also, the maximum number of items shown in a ContextMenu is now 16,
which looks quite a bit nicer than showing 30 or so all at once.

ScrollBarWidget now looks a little nicer, as the scroll bar is a little
wider, and the arrows are larger (same ones from ContextMenu).  As well,
click and hold works on the scroll buttons, even during mouse movement.
This means you no longer have to hold the mouse completely still to
get continuous mouse click events.

Dialogs can now register whether they want to receive continuous mouse
click events (aka, click and hold of a mouse button).  Most dialogs
don't need it, and some even work incorrectly when it's activated.
This fixes a bug in ContextMenu, where click and hold on a PopupWidget
would continuously open and close the list of items.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2080 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2010-07-30 17:25:28 +00:00
parent c9cae47787
commit 1b19d4a29f
9 changed files with 151 additions and 91 deletions

View File

@ -37,6 +37,9 @@ ContextMenu::ContextMenu(GuiObject* boss, const GUI::Font& font,
_selectedOffset(0),
_selectedItem(-1),
_showScroll(false),
_isScrolling(false),
_scrollUpColor(kColor),
_scrollDnColor(kColor),
_font(&font),
_cmd(cmd),
_xorig(0),
@ -69,6 +72,10 @@ void ContextMenu::addItems(const StringMap& items)
_x = _y = 0;
_w = maxwidth + 10;
_h = 1; // recalculate this in ::recalc()
_scrollUpColor = _firstEntry > 0 ? kScrollColor : kColor;
_scrollDnColor = (_firstEntry + _numEntries < (int)_entries.size()) ?
kScrollColor : kColor;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -104,7 +111,7 @@ void ContextMenu::recalc(const GUI::Rect& image)
{
// Now is the time to adjust the height
// If it's higher than the screen, we need to scroll through
int maxentries = (image.height() - 4) / _rowHeight;
int maxentries = BSPF_min(16, (image.height() - 4) / _rowHeight);
if((int)_entries.size() > maxentries)
{
// We show two less than the max, so we have room for two scroll buttons
@ -118,6 +125,7 @@ void ContextMenu::recalc(const GUI::Rect& image)
_h = _entries.size() * _rowHeight + 4;
_showScroll = false;
}
_isScrolling = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -185,12 +193,17 @@ const string& ContextMenu::getSelectedTag() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ContextMenu::handleMouseDown(int x, int y, int button, int clickCount)
{
// Compute over which item the mouse is...
int item = findItem(x, y);
// Only do a selection when the left button is in the dialog
if(button == 1)
{
x += getAbsX(); y += getAbsY();
if(x >= _x && x <= _x+_w && y >= _y && y <= _y+_h)
if(item != -1)
{
_isScrolling = _showScroll && ((item == 0) || (item == _numEntries+1));
sendSelection();
}
else
close();
}
@ -208,6 +221,13 @@ void ContextMenu::handleMouseMoved(int x, int y, int button)
drawCurrentSelection(item);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool ContextMenu::handleMouseClicks(int x, int y, int button)
{
// Let continuous mouse clicks come through, as the scroll buttons need them
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ContextMenu::handleKeyDown(int ascii, int keycode, int modifiers)
{
@ -288,6 +308,8 @@ void ContextMenu::sendSelection()
return scrollUp();
else if(_selectedOffset == _numEntries+1) // scroll down
return scrollDown();
else if(_isScrolling)
return;
else
item--;
}
@ -349,6 +371,8 @@ void ContextMenu::scrollUp()
if(_firstEntry > 0)
{
_firstEntry--;
_scrollUpColor = _firstEntry > 0 ? kScrollColor : kColor;
_scrollDnColor = kScrollColor;
setDirty();
}
}
@ -359,6 +383,9 @@ void ContextMenu::scrollDown()
if(_firstEntry + _numEntries < (int)_entries.size())
{
_firstEntry++;
_scrollUpColor = kScrollColor;
_scrollDnColor = (_firstEntry + _numEntries < (int)_entries.size()) ?
kScrollColor : kColor;
setDirty();
}
}
@ -406,7 +433,7 @@ void ContextMenu::drawDialog()
if(_showScroll)
{
s.hLine(x, y+_rowHeight-1, w+2, kShadowColor);
s.drawBitmap(up_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, kScrollColor, 8);
s.drawBitmap(up_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, _scrollUpColor, 8);
y += _rowHeight;
offset--;
}
@ -424,7 +451,7 @@ void ContextMenu::drawDialog()
if(_showScroll)
{
s.hLine(x, y, w+2, kShadowColor);
s.drawBitmap(down_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, kScrollColor, 8);
s.drawBitmap(down_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, _scrollDnColor, 8);
}
s.addDirtyRect(_x, _y, _w, _h);

View File

@ -76,6 +76,7 @@ class ContextMenu : public Dialog, public CommandSender
protected:
void handleMouseDown(int x, int y, int button, int clickCount);
void handleMouseMoved(int x, int y, int button);
bool handleMouseClicks(int x, int y, int button);
void handleKeyDown(int ascii, int keycode, int modifiers); // Scroll through entries with arrow keys etc
void handleJoyDown(int stick, int button);
void handleJoyAxis(int stick, int axis, int value);
@ -103,6 +104,8 @@ class ContextMenu : public Dialog, public CommandSender
int _firstEntry, _numEntries;
int _selectedOffset, _selectedItem;
bool _showScroll;
bool _isScrolling;
uInt32 _scrollUpColor, _scrollDnColor;
const GUI::Font* _font;
int _cmd;

View File

@ -44,7 +44,7 @@ Dialog::Dialog(OSystem* instance, DialogContainer* parent,
_dragWidget(0),
_okWidget(0),
_cancelWidget(0),
_visible(true),
_visible(false),
_isBase(isBase),
_ourTab(NULL),
_surface(NULL),
@ -106,6 +106,8 @@ void Dialog::close()
releaseFocus();
parent().removeDialog();
_visible = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -279,56 +281,6 @@ void Dialog::drawDialog()
s.update();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleMouseDown(int x, int y, int button, int clickCount)
{
Widget* w;
w = findWidget(x, y);
_dragWidget = w;
setFocus(w);
if(w)
w->handleMouseDown(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button, clickCount);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleMouseUp(int x, int y, int button, int clickCount)
{
Widget* w;
if(_focusedWidget)
{
// Lose focus on mouseup unless the widget requested to retain the focus
if(! (_focusedWidget->getFlags() & WIDGET_RETAIN_FOCUS ))
releaseFocus();
}
w = _dragWidget;
if(w)
w->handleMouseUp(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button, clickCount);
_dragWidget = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleMouseWheel(int x, int y, int direction)
{
Widget* w;
// This may look a bit backwards, but I think it makes more sense for
// the mouse wheel to primarily affect the widget the mouse is at than
// the widget that happens to be focused.
w = findWidget(x, y);
if(!w)
w = _focusedWidget;
if (w)
w->handleMouseWheel(x, y, direction);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleKeyDown(int ascii, int keycode, int modifiers)
{
@ -383,6 +335,56 @@ void Dialog::handleKeyUp(int ascii, int keycode, int modifiers)
_focusedWidget->handleKeyUp(ascii, keycode, modifiers);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleMouseDown(int x, int y, int button, int clickCount)
{
Widget* w;
w = findWidget(x, y);
_dragWidget = w;
setFocus(w);
if(w)
w->handleMouseDown(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button, clickCount);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleMouseUp(int x, int y, int button, int clickCount)
{
Widget* w;
if(_focusedWidget)
{
// Lose focus on mouseup unless the widget requested to retain the focus
if(! (_focusedWidget->getFlags() & WIDGET_RETAIN_FOCUS ))
releaseFocus();
}
w = _dragWidget;
if(w)
w->handleMouseUp(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button, clickCount);
_dragWidget = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleMouseWheel(int x, int y, int direction)
{
Widget* w;
// This may look a bit backwards, but I think it makes more sense for
// the mouse wheel to primarily affect the widget the mouse is at than
// the widget that happens to be focused.
w = findWidget(x, y);
if(!w)
w = _focusedWidget;
if (w)
w->handleMouseWheel(x, y, direction);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleMouseMoved(int x, int y, int button)
{
@ -433,6 +435,17 @@ void Dialog::handleMouseMoved(int x, int y, int button)
w->handleMouseMoved(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Dialog::handleMouseClicks(int x, int y, int button)
{
Widget* w = findWidget(x, y);
if(w)
return w->handleMouseClicks(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button);
else
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleJoyDown(int stick, int button)
{

View File

@ -88,12 +88,12 @@ class Dialog : public GuiObject
virtual void handleMouseUp(int x, int y, int button, int clickCount);
virtual void handleMouseWheel(int x, int y, int direction);
virtual void handleMouseMoved(int x, int y, int button);
virtual bool handleMouseClicks(int x, int y, int button);
virtual void handleJoyDown(int stick, int button);
virtual void handleJoyUp(int stick, int button);
virtual void handleJoyAxis(int stick, int axis, int value);
virtual bool handleJoyHat(int stick, int hat, int value);
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id);
virtual void handleScreenChanged() {}
Widget* findWidget(int x, int y); // Find the widget at pos x,y if any

View File

@ -189,9 +189,6 @@ void DialogContainer::handleMouseMotionEvent(int x, int y, int button)
activeDialog->handleMouseMoved(x - activeDialog->_x,
y - activeDialog->_y,
button);
// Turn off continuous click events as soon as the mouse moves
myCurrentMouseDown.button = -1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -231,11 +228,18 @@ void DialogContainer::handleMouseButtonEvent(MouseButton b, int x, int y, uInt8
}
myLastClick.time = myTime;
// Now account for repeated mouse events (click and hold)
myCurrentMouseDown.x = x;
myCurrentMouseDown.y = y;
myCurrentMouseDown.button = button;
myClickRepeatTime = myTime + kRepeatInitialDelay;
// Now account for repeated mouse events (click and hold), but only
// if the dialog wants them
if(activeDialog->handleMouseClicks(x - activeDialog->_x, y - activeDialog->_y,
button))
{
myCurrentMouseDown.x = x;
myCurrentMouseDown.y = y;
myCurrentMouseDown.button = button;
myClickRepeatTime = myTime + kRepeatInitialDelay;
}
else
myCurrentMouseDown.button = -1;
activeDialog->handleMouseDown(x - activeDialog->_x, y - activeDialog->_y,
button, myLastClick.count);

View File

@ -79,7 +79,7 @@ PopUpWidget::~PopUpWidget()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::handleMouseDown(int x, int y, int button, int clickCount)
{
if(isEnabled())
if(isEnabled() && !myMenu->isVisible())
{
// Add menu just underneath parent widget
const GUI::Rect& image = instance().frameBuffer().imageRect();

View File

@ -33,30 +33,30 @@
* and we thus should not highlight the arrows/slider.
*/
#define UP_DOWN_BOX_HEIGHT 12
#define UP_DOWN_BOX_HEIGHT 18
// Up arrow
static unsigned int up_arrow[8] = {
0x00011000,
0x00011000,
0x00111100,
0x00111100,
0x01111110,
0x01111110,
0x11111111,
0x00000000,
0x00000000,
0x00000000,
0x00000000
0x11111111
};
// Down arrow
static unsigned int down_arrow[8] = {
0x11111111,
0x01111110,
0x00111100,
0x00011000,
0x00000000,
0x00000000,
0x00000000,
0x00000000
0x11111111,
0x11111111,
0x01111110,
0x01111110,
0x00111100,
0x00111100,
0x00011000,
0x00011000
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -83,6 +83,10 @@ ScrollBarWidget::ScrollBarWidget(GuiObject* boss, const GUI::Font& font,
void ScrollBarWidget::handleMouseDown(int x, int y, int button,
int clickCount)
{
// Ignore subsequent mouse clicks when the slider is being moved
if(_draggingPart == kSliderPart)
return;
int old_pos = _currentPos;
// Do nothing if there are less items than fit on one page
@ -187,6 +191,13 @@ void ScrollBarWidget::handleMouseMoved(int x, int y, int button)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool ScrollBarWidget::handleMouseClicks(int x, int y, int button)
{
// Let continuous mouse clicks come through, as the scroll buttons need them
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ScrollBarWidget::checkBounds(int old_pos)
{
@ -257,13 +268,13 @@ void ScrollBarWidget::drawWidget(bool hilite)
// Up arrow
s.frameRect(_x, _y, _w, UP_DOWN_BOX_HEIGHT, kColor);
s.drawBitmap(up_arrow, _x+2, _y+4, isSinglePage ? kColor :
(hilite && _part == kUpArrowPart) ? kScrollColorHi : kScrollColor, 4);
s.drawBitmap(up_arrow, _x+3, _y+5, isSinglePage ? kColor :
(hilite && _part == kUpArrowPart) ? kScrollColorHi : kScrollColor, 8);
// Down arrow
s.frameRect(_x, bottomY - UP_DOWN_BOX_HEIGHT, _w, UP_DOWN_BOX_HEIGHT, kColor);
s.drawBitmap(down_arrow, _x+2, bottomY - UP_DOWN_BOX_HEIGHT + 4, isSinglePage ? kColor :
(hilite && _part == kDownArrowPart) ? kScrollColorHi : kScrollColor);
s.drawBitmap(down_arrow, _x+3, bottomY - UP_DOWN_BOX_HEIGHT + 5, isSinglePage ? kColor :
(hilite && _part == kDownArrowPart) ? kScrollColorHi : kScrollColor, 8);
// Slider
if(!isSinglePage)

View File

@ -29,7 +29,7 @@
#include "bspf.hxx"
enum {
kScrollBarWidth = 12
kScrollBarWidth = 14
};
class ScrollBarWidget : public Widget, public CommandSender
@ -44,6 +44,7 @@ class ScrollBarWidget : public Widget, public CommandSender
virtual void handleMouseUp(int x, int y, int button, int clickCount);
virtual void handleMouseWheel(int x, int y, int direction);
virtual void handleMouseMoved(int x, int y, int button);
virtual bool handleMouseClicks(int x, int y, int button);
virtual void handleMouseEntered(int button);
virtual void handleMouseLeft(int button);

View File

@ -97,14 +97,15 @@ class Widget : public GuiObject
virtual int getAbsX() const { return _x + _boss->getChildX(); }
virtual int getAbsY() const { return _y + _boss->getChildY(); }
virtual bool handleKeyDown(int ascii, int keycode, int modifiers) { return false; }
virtual bool handleKeyUp(int ascii, int keycode, int modifiers) { return false; }
virtual void handleMouseDown(int x, int y, int button, int clickCount) {}
virtual void handleMouseUp(int x, int y, int button, int clickCount) {}
virtual void handleMouseEntered(int button) {}
virtual void handleMouseLeft(int button) {}
virtual void handleMouseMoved(int x, int y, int button) {}
virtual void handleMouseWheel(int x, int y, int direction) {}
virtual bool handleKeyDown(int ascii, int keycode, int modifiers) { return false; }
virtual bool handleKeyUp(int ascii, int keycode, int modifiers) { return false; }
virtual bool handleMouseClicks(int x, int y, int button) { return false; }
virtual void handleJoyDown(int stick, int button) {}
virtual void handleJoyUp(int stick, int button) {}
virtual void handleJoyAxis(int stick, int axis, int value) {}
@ -123,11 +124,11 @@ class Widget : public GuiObject
void clearFlags(int flags) { _flags &= ~flags; }
int getFlags() const { return _flags; }
bool isEnabled() const { return _flags & WIDGET_ENABLED; }
bool isVisible() const { return !(_flags & WIDGET_INVISIBLE); }
bool wantsFocus() const { return _flags & WIDGET_RETAIN_FOCUS; }
bool wantsTab() const { return _flags & WIDGET_WANTS_TAB; }
bool wantsRaw() const { return _flags & WIDGET_WANTS_RAWDATA; }
bool isEnabled() const { return _flags & WIDGET_ENABLED; }
bool isVisible() const { return !(_flags & WIDGET_INVISIBLE); }
bool wantsFocus() const { return _flags & WIDGET_RETAIN_FOCUS; }
bool wantsTab() const { return _flags & WIDGET_WANTS_TAB; }
bool wantsRaw() const { return _flags & WIDGET_WANTS_RAWDATA; }
void setID(int id) { _id = id; }
int getID() { return _id; }