added latest of painelf's patches which makes NewGui handle system events directly (code becomes much cleaner and more powerful this way); he also implemented a 'focus' item in NewGui; atop of this several changes of my own that further improve the GUI behaviour

svn-id: r4541
This commit is contained in:
Max Horn 2002-07-13 22:41:29 +00:00
parent 1238d74227
commit c3c11b79cb
10 changed files with 159 additions and 100 deletions

View file

@ -27,11 +27,7 @@
/* /*
* TODO: * TODO:
* - The handleKey method of widgets is currently never called, code for that has * - Implement scrolling using arrow keys, pageup/pagedown, home/end keys etc.
* to be added to dialog.cpp
* - Once the above item is done, implement scrolling using arrow keys,
* pageup/pagedown, home/end keys etc.
* - Allow user to select an entry w/ the mouse
* - Implement editing of the selected string in a generic fashion * - Implement editing of the selected string in a generic fashion
*/ */

View file

@ -60,11 +60,11 @@ static uint32 down_arrow[8] = {
ScrollBarWidget::ScrollBarWidget(Dialog *boss, int x, int y, int w, int h) ScrollBarWidget::ScrollBarWidget(Dialog *boss, int x, int y, int w, int h)
: Widget (boss, x, y, w, h), CommandSender(boss) : Widget (boss, x, y, w, h), CommandSender(boss)
{ {
_flags = WIDGET_ENABLED | WIDGET_TRACK_MOUSE | WIDGET_CLEARBG; _flags = WIDGET_ENABLED | WIDGET_TRACK_MOUSE | WIDGET_CLEARBG | WIDGET_WANT_TICKLE;
_type = kScrollBarWidget; _type = kScrollBarWidget;
_part = kNoPart; _part = kNoPart;
_isDraggingSlider = false; _draggingPart = kNoPart;
} }
@ -75,37 +75,33 @@ void ScrollBarWidget::handleMouseDown(int x, int y, int button)
if (y <= UP_DOWN_BOX_HEIGHT) { if (y <= UP_DOWN_BOX_HEIGHT) {
// Up arrow // Up arrow
_currentPos--; _currentPos--;
_draggingPart = kUpArrowPart;
} else if (y >= _h - UP_DOWN_BOX_HEIGHT) { } else if (y >= _h - UP_DOWN_BOX_HEIGHT) {
// Down arrow // Down arrow
_currentPos++; _currentPos++;
_draggingPart = kDownArrowPart;
} else if (y < _sliderPos) { } else if (y < _sliderPos) {
_currentPos -= _entriesPerPage; _currentPos -= _entriesPerPage;
} else if (y >= _sliderPos + _sliderHeight) { } else if (y >= _sliderPos + _sliderHeight) {
_currentPos += _entriesPerPage; _currentPos += _entriesPerPage;
} else { } else {
_isDraggingSlider = true; _draggingPart = kSliderPart;
_sliderDeltaMouseDownPos = y - _sliderPos; _sliderDeltaMouseDownPos = y - _sliderPos;
} }
// Make sure that _currentPos is still inside the bounds // Make sure that _currentPos is still inside the bounds
checkbounds(); checkBounds(old_pos);
if (old_pos != _currentPos) {
recalc();
draw();
sendCommand(kSetPositionCmd, _currentPos);
}
} }
void ScrollBarWidget::handleMouseUp(int x, int y, int button) void ScrollBarWidget::handleMouseUp(int x, int y, int button)
{ {
if (_isDraggingSlider) if (_draggingPart != kNoPart)
_isDraggingSlider = false; _draggingPart = kNoPart;
} }
void ScrollBarWidget::handleMouseMoved(int x, int y, int button) void ScrollBarWidget::handleMouseMoved(int x, int y, int button)
{ {
if (_isDraggingSlider) { if (_draggingPart == kSliderPart) {
int old_pos = _currentPos; int old_pos = _currentPos;
_sliderPos = y - _sliderDeltaMouseDownPos; _sliderPos = y - _sliderDeltaMouseDownPos;
@ -118,12 +114,7 @@ void ScrollBarWidget::handleMouseMoved(int x, int y, int button)
_currentPos = _currentPos =
(_sliderPos - UP_DOWN_BOX_HEIGHT) * (_numEntries - _entriesPerPage) / (_h - _sliderHeight - (_sliderPos - UP_DOWN_BOX_HEIGHT) * (_numEntries - _entriesPerPage) / (_h - _sliderHeight -
2 * UP_DOWN_BOX_HEIGHT); 2 * UP_DOWN_BOX_HEIGHT);
checkbounds(); checkBounds(old_pos);
if (_currentPos != old_pos) {
draw();
sendCommand(kSetPositionCmd, _currentPos);
}
} else { } else {
int old_part = _part; int old_part = _part;
@ -143,12 +134,39 @@ void ScrollBarWidget::handleMouseMoved(int x, int y, int button)
} }
} }
void ScrollBarWidget::checkbounds() void ScrollBarWidget::handleTickle()
{
/*
// FIXME - this code is supposed to allow for "click-repeat" (like key repeat),
// i.e. if you click on one of the arrows and keep clicked, it will scroll
// continously. However, just like key repeat, this requires two delays:
// First an "initial" delay that has to pass before repeating starts (otherwise
// it is near to impossible to achieve single clicks). Secondly, a repeat delay
// that determines how often per second a click is simulated.
int old_pos = _currentPos;
if (_draggingPart == kUpArrowPart)
_currentPos--;
else if (_draggingPart == kDownArrowPart)
_currentPos++;
// Make sure that _currentPos is still inside the bounds
checkBounds(old_pos);
*/
}
void ScrollBarWidget::checkBounds(int old_pos)
{ {
if (_currentPos > _numEntries - _entriesPerPage) if (_currentPos > _numEntries - _entriesPerPage)
_currentPos = _numEntries - _entriesPerPage; _currentPos = _numEntries - _entriesPerPage;
else if (_currentPos < 0) else if (_currentPos < 0)
_currentPos = 0; _currentPos = 0;
if (old_pos != _currentPos) {
recalc();
draw();
sendCommand(kSetPositionCmd, _currentPos);
}
} }
void ScrollBarWidget::recalc() void ScrollBarWidget::recalc()
@ -172,6 +190,9 @@ void ScrollBarWidget::drawWidget(bool hilite)
gui->frameRect(_x, _y, _w, _h, gui->_shadowcolor); gui->frameRect(_x, _y, _w, _h, gui->_shadowcolor);
if (_draggingPart != kNoPart)
_part = _draggingPart;
// Up arrow // Up arrow
gui->frameRect(_x, _y, _w, UP_DOWN_BOX_HEIGHT, gui->_color); gui->frameRect(_x, _y, _w, UP_DOWN_BOX_HEIGHT, gui->_color);
gui->drawBitmap(up_arrow, _x, _y, gui->drawBitmap(up_arrow, _x, _y,

View file

@ -27,6 +27,7 @@ enum {
kScrollBarWidth = 9 kScrollBarWidth = 9
}; };
enum { enum {
kSetPositionCmd = 'SETP' kSetPositionCmd = 'SETP'
}; };
@ -34,24 +35,27 @@ enum {
class ScrollBarWidget : public Widget, public CommandSender { class ScrollBarWidget : public Widget, public CommandSender {
protected: protected:
enum { typedef enum {
kNoPart, kNoPart,
kUpArrowPart, kUpArrowPart,
kDownArrowPart, kDownArrowPart,
kSliderPart, kSliderPart,
kPageUpPart, kPageUpPart,
kPageDownPart kPageDownPart
} _part; } Part;
Part _part;
int _sliderHeight; int _sliderHeight;
int _sliderPos; int _sliderPos;
bool _isDraggingSlider; Part _draggingPart;
int _sliderDeltaMouseDownPos; int _sliderDeltaMouseDownPos;
public: public:
int _numEntries; int _numEntries;
int _entriesPerPage; int _entriesPerPage;
int _currentPos; int _currentPos;
public: public:
ScrollBarWidget(Dialog *boss, int x, int y, int w, int h); ScrollBarWidget(Dialog *boss, int x, int y, int w, int h);
@ -60,13 +64,16 @@ public:
void handleMouseMoved(int x, int y, int button); void handleMouseMoved(int x, int y, int button);
void handleMouseEntered(int button) { setFlags(WIDGET_HILITED); } void handleMouseEntered(int button) { setFlags(WIDGET_HILITED); }
void handleMouseLeft(int button) { clearFlags(WIDGET_HILITED); _part = kNoPart; draw(); } void handleMouseLeft(int button) { clearFlags(WIDGET_HILITED); _part = kNoPart; draw(); }
void handleTickle();
// FIXME: Shouldn't these be private? // FIXME - this should be private, but then we also have to add accessors
// for _numEntries, _entriesPerPage and _currentPos. This again leads to the question:
// should these accessors force a redraw?
void recalc(); void recalc();
protected: protected:
void drawWidget(bool hilite); void drawWidget(bool hilite);
void checkbounds(); void checkBounds(int old_pos);
}; };

View file

@ -41,15 +41,6 @@ void Dialog::setupScreenBuf()
_gui->blendRect(_x, _y, _w, _h, _gui->_bgcolor); _gui->blendRect(_x, _y, _w, _h, _gui->_bgcolor);
_gui->box(_x, _y, _w, _h); _gui->box(_x, _y, _w, _h);
// Draw a bgcolor rectangle for all widgets which have WIDGET_CLEARBG set.
Widget *w = _firstWidget;
while (w) {
if (w->_flags & WIDGET_CLEARBG)
_gui->fillRect(_x + w->_x, _y + w->_y, w->_w, w->_h, _gui->_bgcolor);
// FIXME - should we also draw borders here if WIDGET_BORDER is set?
w = w->_next;
}
// Finally blit this to _screenBuf // Finally blit this to _screenBuf
_gui->blitTo(_screenBuf, _x, _y, _w, _h); _gui->blitTo(_screenBuf, _x, _y, _w, _h);
} }
@ -82,18 +73,30 @@ void Dialog::draw()
void Dialog::handleMouseDown(int x, int y, int button) void Dialog::handleMouseDown(int x, int y, int button)
{ {
// FIXME: If outside focused widget, widget loses focus _focusedWidget = findWidget(x, y);
Widget *w = findWidget(x - _x, y - _y); if (_focusedWidget) {
if (w) _focusedWidget->handleMouseDown(x - _focusedWidget->_x, y - _focusedWidget->_y, button);
w->handleMouseDown(x - _x - w->_x, y - _y - w->_y, button); }
} }
void Dialog::handleMouseUp(int x, int y, int button) void Dialog::handleMouseUp(int x, int y, int button)
{ {
Widget *w = findWidget(x - _x, y - _y); Widget *w;
if (_focusedWidget) {
w = _focusedWidget;
// Lose focus on mouseup unless the widget requested to retain the focus
if (! (_focusedWidget->getFlags() & WIDGET_RETAIN_FOCUS ))
_focusedWidget = 0;
} else {
w = findWidget(x, y);
}
if (w) if (w)
w->handleMouseUp(x - _x - w->_x, y - _y - w->_y, button); w->handleMouseUp(x - w->_x, y - w->_y, button);
} }
void Dialog::handleKeyDown(char key, int modifiers) void Dialog::handleKeyDown(char key, int modifiers)
@ -102,53 +105,76 @@ void Dialog::handleKeyDown(char key, int modifiers)
if (key == 27) if (key == 27)
close(); close();
// FIXME: Only if not focused widget if (_focusedWidget) {
_focusedWidget->handleKeyDown(key, modifiers);
// Hotkey handling } else {
Widget *w = _firstWidget; // Hotkey handling
key = toupper(key); Widget *w = _firstWidget;
while (w) { key = toupper(key);
if (w->_type == kButtonWidget && key == toupper(((ButtonWidget *)w)->_hotkey)) { while (w) {
// FIXME: Calling both handlers is bad style, but we don't really know which one we're supposed to call if (w->_type == kButtonWidget && key == toupper(((ButtonWidget *)w)->_hotkey)) {
w->handleMouseDown(0, 0, 1); // We first send a mouseDown then a mouseUp.
w->handleMouseUp(0, 0, 1); // FIXME: insert a brief delay between both.
break; w->handleMouseDown(0, 0, 1);
w->handleMouseUp(0, 0, 1);
break;
}
w = w->_next;
} }
w = w->_next;
} }
// TODO - introduce the notion of a "focused" widget which receives
// key events (by calling its handleKey method). Required for editfields
// and also for an editable list widget.
} }
void Dialog::handleKeyUp(char key, int modifiers) void Dialog::handleKeyUp(char key, int modifiers)
{ {
// FIXME: Focused widget recieves keyup // Focused widget recieves keyup events
if (_focusedWidget)
_focusedWidget->handleKeyUp(key, modifiers);
} }
void Dialog::handleMouseMoved(int x, int y, int button) void Dialog::handleMouseMoved(int x, int y, int button)
{ {
Widget *w = findWidget(x - _x, y - _y); Widget *w;
if (_mouseWidget != w) { if (_focusedWidget) {
if (_mouseWidget) w = _focusedWidget;
_mouseWidget->handleMouseLeft(button);
if (w) // We still send mouseEntered/Left messages to the focused item
// (but to no other items).
bool mouseInFocusedWidget = (x >= w->_x && x < w->_x + w->_w && y >= w->_y && y < w->_y + w->_h);
if (mouseInFocusedWidget && _mouseWidget != w) {
_mouseWidget = w;
w->handleMouseEntered(button); w->handleMouseEntered(button);
_mouseWidget = w; } else if (!mouseInFocusedWidget && _mouseWidget == w) {
_mouseWidget = 0;
w->handleMouseLeft(button);
}
} else {
w = findWidget(x, y);
if (_mouseWidget != w) {
if (_mouseWidget)
_mouseWidget->handleMouseLeft(button);
if (w)
w->handleMouseEntered(button);
_mouseWidget = w;
}
if (!w || !(w->getFlags() & WIDGET_TRACK_MOUSE)) {
return;
}
} }
if (!w) w->handleMouseMoved(x - w->_x, y - w->_y, button);
return;
if (w->getFlags() & WIDGET_TRACK_MOUSE) {
w->handleMouseMoved(x - _x - w->_x, y - _y - w->_y, button);
}
//FIXME: Focused widget recieves mouseup
} }
void Dialog::handleTickle()
{
// Focused widget recieves tickle notifications
if (_focusedWidget && _focusedWidget->getFlags() & WIDGET_WANT_TICKLE) {
_focusedWidget->handleTickle();
}
}
void Dialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) void Dialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
{ {
@ -167,8 +193,8 @@ Widget *Dialog::findWidget(int x, int y)
{ {
Widget *w = _firstWidget; Widget *w = _firstWidget;
while (w) { while (w) {
// Stop as soon as we find a fidget that contains (x,y) // Stop as soon as we find a widget that contains the point (x,y)
if (x >= w->_x && x <= w->_x + w->_w && y >= w->_y && y <= w->_y + w->_h) if (x >= w->_x && x < w->_x + w->_w && y >= w->_y && y < w->_y + w->_h)
break; break;
w = w->_next; w = w->_next;
} }
@ -177,6 +203,8 @@ Widget *Dialog::findWidget(int x, int y)
void Dialog::close() void Dialog::close()
{ {
_mouseWidget = 0;
_focusedWidget = 0;
_gui->closeTopDialog(); _gui->closeTopDialog();
} }

View file

@ -36,27 +36,30 @@ enum {
class Dialog : public CommandReceiver { class Dialog : public CommandReceiver {
friend class Widget; friend class Widget;
friend class NewGui;
protected: protected:
NewGui *_gui; NewGui *_gui;
Widget *_firstWidget;
int16 _x, _y; int16 _x, _y;
uint16 _w, _h; uint16 _w, _h;
Widget *_firstWidget;
Widget *_mouseWidget; Widget *_mouseWidget;
Widget *_focusedWidget;
byte *_screenBuf; byte *_screenBuf;
public: public:
Dialog(NewGui *gui, int x, int y, int w, int h) Dialog(NewGui *gui, int x, int y, int w, int h)
: _gui(gui), _firstWidget(0), _x(x), _y(y), _w(w), _h(h), _mouseWidget(0), _screenBuf(0) : _gui(gui), _x(x), _y(y), _w(w), _h(h), _firstWidget(0),
_mouseWidget(0), _focusedWidget(0), _screenBuf(0)
{} {}
virtual ~Dialog(); virtual ~Dialog();
virtual void draw(); virtual void draw();
//virtual void handleIdle(); // Called periodically virtual void handleTickle(); // Called periodically (in every guiloop() )
virtual void handleMouseDown(int x, int y, int button); virtual void handleMouseDown(int x, int y, int button);
virtual void handleMouseUp(int x, int y, int button); virtual void handleMouseUp(int x, int y, int button);
virtual void handleKeyDown(char key, int modifiers); // modifiers = alt/shift/ctrl etc. virtual void handleKeyDown(char key, int modifiers);
virtual void handleKeyUp(char key, int modifiers); // modifiers = alt/shift/ctrl etc. virtual void handleKeyUp(char key, int modifiers);
virtual void handleMouseMoved(int x, int y, int button); virtual void handleMouseMoved(int x, int y, int button);
virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data); virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);

View file

@ -34,7 +34,7 @@ Widget::Widget (Dialog *boss, int x, int y, int w, int h)
void Widget::draw() void Widget::draw()
{ {
NewGui *gui = _boss->_gui; NewGui *gui = _boss->getGui();
if (_flags & WIDGET_INVISIBLE) if (_flags & WIDGET_INVISIBLE)
return; return;
@ -127,9 +127,9 @@ ButtonWidget::~ButtonWidget()
} }
} }
void ButtonWidget::handleMouseDown(int x, int y, int button) void ButtonWidget::handleMouseUp(int x, int y, int button)
{ {
if (_flags & WIDGET_ENABLED) if (_flags & WIDGET_ENABLED && x >= 0 && x < _w && y >= 0 && y < _h)
sendCommand(_cmd, 0); sendCommand(_cmd, 0);
} }

View file

@ -32,7 +32,9 @@ enum {
WIDGET_BORDER = 1 << 3, WIDGET_BORDER = 1 << 3,
WIDGET_CLEARBG = 1 << 4, WIDGET_CLEARBG = 1 << 4,
WIDGET_WANT_TICKLE = 1 << 5, WIDGET_WANT_TICKLE = 1 << 5,
WIDGET_TRACK_MOUSE = 1 << 6 WIDGET_TRACK_MOUSE = 1 << 6,
WIDGET_RETAIN_FOCUS = 1 << 7 // Retain focus on mouse up. By default widgets lose focus on mouseup, but some widgets might want to retain it - widgets where you enter text, for instance
}; };
enum { enum {
@ -75,13 +77,14 @@ public:
class Widget { class Widget {
friend class Dialog; friend class Dialog;
protected: protected:
int _type; uint32 _type;
Dialog *_boss; Dialog *_boss;
Widget *_next; Widget *_next;
int16 _x, _y; int16 _x, _y;
uint16 _w, _h; uint16 _w, _h;
uint16 _id; uint16 _id;
int _flags; uint16 _flags;
public: public:
Widget(Dialog *boss, int x, int y, int w, int h); Widget(Dialog *boss, int x, int y, int w, int h);
virtual ~Widget() {} virtual ~Widget() {}
@ -93,6 +96,7 @@ public:
virtual void handleMouseMoved(int x, int y, int button) {} virtual void handleMouseMoved(int x, int y, int button) {}
virtual void handleKeyDown(char key, int modifiers) {} virtual void handleKeyDown(char key, int modifiers) {}
virtual void handleKeyUp(char key, int modifiers) {} virtual void handleKeyUp(char key, int modifiers) {}
virtual void handleTickle() {}
void draw(); void draw();
void setFlags(int flags) { _flags |= flags; } void setFlags(int flags) { _flags |= flags; }
@ -132,7 +136,7 @@ public:
void setCmd(uint32 cmd) { _cmd = cmd; } void setCmd(uint32 cmd) { _cmd = cmd; }
uint32 getCmd() const { return _cmd; } uint32 getCmd() const { return _cmd; }
void handleMouseDown(int x, int y, int button); void handleMouseUp(int x, int y, int button);
void handleMouseEntered(int button) { setFlags(WIDGET_HILITED); draw(); } void handleMouseEntered(int button) { setFlags(WIDGET_HILITED); draw(); }
void handleMouseLeft(int button) { clearFlags(WIDGET_HILITED); draw(); } void handleMouseLeft(int button) { clearFlags(WIDGET_HILITED); draw(); }
}; };

View file

@ -80,7 +80,7 @@ void NewGui::loop()
saveState(); saveState();
if (_use_alpha_blending) if (_use_alpha_blending)
activeDialog->setupScreenBuf(); activeDialog->setupScreenBuf();
#if 1 #if 0
// FIXME - hack to encode our own custom GUI colors. Since we have to live // FIXME - hack to encode our own custom GUI colors. Since we have to live
// with a given 8 bit palette, the result is not always as nice as one // with a given 8 bit palette, the result is not always as nice as one
// would wish, but this is just an experiment after all. // would wish, but this is just an experiment after all.
@ -95,6 +95,8 @@ void NewGui::loop()
_prepare_for_gui = false; _prepare_for_gui = false;
} }
activeDialog->handleTickle();
if (_need_redraw) { if (_need_redraw) {
activeDialog->draw(); activeDialog->draw();
_need_redraw = false; _need_redraw = false;
@ -118,16 +120,16 @@ void NewGui::loop()
// activeDialog->handleKeyUp(t.kbd.ascii, t.kbd.flags); // activeDialog->handleKeyUp(t.kbd.ascii, t.kbd.flags);
break; break;
case OSystem::EVENT_MOUSEMOVE: case OSystem::EVENT_MOUSEMOVE:
activeDialog->handleMouseMoved(t.mouse.x, t.mouse.y, 0); activeDialog->handleMouseMoved(t.mouse.x - activeDialog->_x, t.mouse.y - activeDialog->_y, 0);
break; break;
// We don't distinguish between mousebuttons (for now at least) // We don't distinguish between mousebuttons (for now at least)
case OSystem::EVENT_LBUTTONDOWN: case OSystem::EVENT_LBUTTONDOWN:
case OSystem::EVENT_RBUTTONDOWN: case OSystem::EVENT_RBUTTONDOWN:
activeDialog->handleMouseDown(t.mouse.x, t.mouse.y, 1); activeDialog->handleMouseDown(t.mouse.x - activeDialog->_x, t.mouse.y - activeDialog->_y, 1);
break; break;
case OSystem::EVENT_LBUTTONUP: case OSystem::EVENT_LBUTTONUP:
case OSystem::EVENT_RBUTTONUP: case OSystem::EVENT_RBUTTONUP:
activeDialog->handleMouseUp(t.mouse.x, t.mouse.y, 1); activeDialog->handleMouseUp(t.mouse.x - activeDialog->_x, t.mouse.y - activeDialog->_y, 1);
break; break;
} }
} }

View file

@ -45,7 +45,6 @@ public:
void pop() { if (_size > 0) _stack[--_size] = 0; } void pop() { if (_size > 0) _stack[--_size] = 0; }
}; };
class EventList { class EventList {
protected: protected:
OSystem::Event _stack[100]; OSystem::Event _stack[100];

View file

@ -1493,7 +1493,6 @@ Scumm *Scumm::createFromDetector(GameDetector *detector, OSystem *syst)
g_mixer = &scumm->_mixer[0]; g_mixer = &scumm->_mixer[0];
/* END HACK */ /* END HACK */
// scumm->_fullScreen = detector->_fullScreen;
scumm->_debugMode = detector->_debugMode; scumm->_debugMode = detector->_debugMode;
scumm->_bootParam = detector->_bootParam; scumm->_bootParam = detector->_bootParam;
scumm->_gameDataPath = detector->_gameDataPath; scumm->_gameDataPath = detector->_gameDataPath;