Initial commit
This commit is contained in:
578
engines/ultima/nuvie/gui/widgets/gui_widget.cpp
Normal file
578
engines/ultima/nuvie/gui/widgets/gui_widget.cpp
Normal file
@@ -0,0 +1,578 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/nuvie/core/nuvie_defs.h"
|
||||
#include "ultima/nuvie/gui/gui.h"
|
||||
#include "ultima/nuvie/gui/widgets/gui_widget.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Nuvie {
|
||||
|
||||
/* Widget constructors */
|
||||
GUI_Widget::GUI_Widget(void *data) {
|
||||
Init(data, 0, 0, 0, 0);
|
||||
}
|
||||
GUI_Widget::GUI_Widget(void *data, int x, int y, int w, int h) {
|
||||
Init(data, x, y, w, h);
|
||||
}
|
||||
|
||||
GUI_Widget::~GUI_Widget() {
|
||||
for (; !children.empty();) {
|
||||
GUI_Widget *child = children.front();
|
||||
|
||||
children.pop_front();
|
||||
delete child;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void GUI_Widget::Init(void *data, int x, int y, int w, int h) {
|
||||
focused = false;
|
||||
gui_drag_manager = nullptr; //set from placeOnScreen method
|
||||
widget_data = data;
|
||||
screen = nullptr;
|
||||
surface = nullptr;
|
||||
SetRect(0, 0, w, h);
|
||||
offset_x = x;
|
||||
offset_y = y;
|
||||
Show();
|
||||
errorptr = nullptr;
|
||||
for (int n = 0; n < 3; ++n) {
|
||||
pressed[n] = 0;
|
||||
}
|
||||
parent = nullptr;
|
||||
|
||||
update_display = true;
|
||||
set_accept_mouseclick(false); // initializes mouseclick time; SB-X
|
||||
delayed_button = Events::BUTTON_NONE; // optional mouseclick-delay; SB-X
|
||||
held_button = Events::BUTTON_NONE; // optional mousedown-delay; SB-X
|
||||
mouse_moved = false;
|
||||
|
||||
int mx = 0, my = 0;
|
||||
if (screen)
|
||||
screen->get_mouse_location(&mx, &my);
|
||||
mouse_over = HitRect(mx, my);
|
||||
}
|
||||
|
||||
int GUI_Widget::AddWidget(GUI_Widget *widget) {
|
||||
children.push_back(widget);
|
||||
widget->setParent(this);
|
||||
|
||||
return 0; //success.
|
||||
}
|
||||
|
||||
|
||||
/* Mark the widget as visible -- this is the default state */
|
||||
void GUI_Widget::Show(void) {
|
||||
status = WIDGET_VISIBLE;
|
||||
}
|
||||
|
||||
/* Mark the widget as hidden; no display, no events */
|
||||
void GUI_Widget::Hide(void) {
|
||||
if (has_focus()) {
|
||||
release_focus();
|
||||
}
|
||||
status = WIDGET_HIDDEN;
|
||||
}
|
||||
|
||||
/* Mark the widget as free, so it will be deleted by the GUI */
|
||||
void GUI_Widget::Delete(void) {
|
||||
status = WIDGET_DELETED;
|
||||
}
|
||||
|
||||
void GUI_Widget::MoveRelative(int dx, int dy) {
|
||||
area.translate(dx, dy);
|
||||
|
||||
for (GUI_Widget *child : children)
|
||||
child->MoveRelative(dx, dy);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void GUI_Widget::Move(int32 new_x, int32 new_y) {
|
||||
area.moveTo(new_x + offset_x, new_y + offset_y);
|
||||
|
||||
for (GUI_Widget *child : children)
|
||||
child->Move(area.left, area.top);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void GUI_Widget::MoveRelativeToParent(int dx, int dy) {
|
||||
area.left = (area.left - offset_x) + dx;
|
||||
area.top = (area.top - offset_y) + dy;
|
||||
|
||||
offset_x = dx;
|
||||
offset_y = dy;
|
||||
|
||||
for (GUI_Widget *child : children)
|
||||
child->Move(area.left, area.top);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void GUI_Widget::grab_focus() {
|
||||
if (GUI::get_gui()->set_focus(this))
|
||||
focused = true;
|
||||
}
|
||||
|
||||
void GUI_Widget::release_focus() {
|
||||
GUI::get_gui()->clear_focus();
|
||||
focused = false;
|
||||
}
|
||||
|
||||
void GUI_Widget::moveToFront() {
|
||||
GUI *gui = GUI::get_gui();
|
||||
if (gui) {
|
||||
gui->removeWidget(this);
|
||||
gui->AddWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
void GUI_Widget::PlaceOnScreen(Screen *s, GUI_DragManager *dm, int x, int y) {
|
||||
if (screen != nullptr)
|
||||
return;
|
||||
|
||||
area.moveTo(x + offset_x, y + offset_y);
|
||||
|
||||
gui_drag_manager = dm;
|
||||
|
||||
SetDisplay(s);
|
||||
|
||||
/* place our children relative to ourself */
|
||||
for (GUI_Widget *child : children)
|
||||
child->PlaceOnScreen(screen, dm, area.left, area.top);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Report status to GUI */
|
||||
WIDGET_status GUI_Widget::Status(void) const {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Set the bounds of the widget.
|
||||
If 'w' or 'h' is -1, that parameter will not be changed.
|
||||
*/
|
||||
void GUI_Widget::SetRect(int x, int y, int w, int h) {
|
||||
area = Common::Rect(x, y, x + w, y + h);
|
||||
}
|
||||
|
||||
void GUI_Widget::SetRect(Common::Rect **bounds) {
|
||||
int minx, maxx;
|
||||
int miny, maxy;
|
||||
int i, v;
|
||||
|
||||
maxx = 0;
|
||||
maxy = 0;
|
||||
for (i = 0; bounds[i]; ++i) {
|
||||
v = (bounds[i]->right - 1);
|
||||
if (maxx < v) {
|
||||
maxx = v;
|
||||
}
|
||||
v = (bounds[i]->bottom - 1);
|
||||
if (maxy < v) {
|
||||
maxy = v;
|
||||
}
|
||||
}
|
||||
minx = maxx;
|
||||
miny = maxy;
|
||||
for (i = 0; bounds[i]; ++i) {
|
||||
v = bounds[i]->left;
|
||||
if (minx > v) {
|
||||
minx = v;
|
||||
}
|
||||
v = bounds[i]->top;
|
||||
if (miny > v) {
|
||||
miny = v;
|
||||
}
|
||||
}
|
||||
SetRect(minx, miny, (maxx - minx + 1), (maxy - miny + 1));
|
||||
}
|
||||
|
||||
/* Check to see if a point intersects the bounds of the widget.
|
||||
*/
|
||||
int GUI_Widget::HitRect(int x, int y) {
|
||||
return HitRect(x, y, area);
|
||||
}
|
||||
|
||||
int GUI_Widget::HitRect(int x, int y, const Common::Rect &rect) {
|
||||
int hit;
|
||||
|
||||
hit = 1;
|
||||
if ((x < rect.left) || (x >= rect.right) ||
|
||||
(y < rect.top) || (y >= rect.bottom)) {
|
||||
hit = 0;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
/* Set the display surface for this widget */
|
||||
void GUI_Widget::SetDisplay(Screen *s) {
|
||||
screen = s;
|
||||
surface = screen->get_sdl_surface();
|
||||
}
|
||||
|
||||
void GUI_Widget::setParent(GUI_Widget *widget) {
|
||||
parent = widget;
|
||||
}
|
||||
|
||||
/* Show the widget.
|
||||
If the surface needs to be locked, it will be locked
|
||||
before this call, and unlocked after it returns.
|
||||
|
||||
****************NO, NOT AT ALL IF I'M NOT TOO DUMB TO LOOK******
|
||||
******OTHERWISE YOU COULDN'T FILLRECT in Display(), ETC!!!! ***********
|
||||
*/
|
||||
void GUI_Widget::Display(bool full_redraw) {
|
||||
DisplayChildren(full_redraw);
|
||||
}
|
||||
|
||||
void GUI_Widget::DisplayChildren(bool full_redraw) {
|
||||
if (update_display)
|
||||
full_redraw = true;
|
||||
|
||||
if (children.empty() == false) {
|
||||
/* display our children */
|
||||
for (GUI_Widget *child : children) {
|
||||
if (child->Status() == WIDGET_VISIBLE)
|
||||
child->Display(full_redraw);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Redraw the widget and only the widget */
|
||||
void GUI_Widget::Redraw(void) {
|
||||
|
||||
if (status == WIDGET_VISIBLE) {
|
||||
update_display = true;
|
||||
if (parent != nullptr)
|
||||
parent->Redraw();
|
||||
//Display();
|
||||
//SDL_UpdateRects(screen,1,&area);
|
||||
}
|
||||
}
|
||||
|
||||
/* GUI idle function -- run when no events pending */
|
||||
// Idle and HandleEvent produce delayed clicks. Don't override if using those. -- SB-X
|
||||
GUI_status GUI_Widget::Idle(void) {
|
||||
if (children.empty() == false) {
|
||||
/* idle our children */
|
||||
for (GUI_Widget *child : children) {
|
||||
GUI_status idleStatus = child->Idle();
|
||||
if (idleStatus != GUI_PASS)
|
||||
return idleStatus;
|
||||
}
|
||||
}
|
||||
if (delayed_button != 0 || held_button != 0)
|
||||
return try_mouse_delayed();
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
/* Widget event handlers.
|
||||
These functions should return a status telling the GUI whether
|
||||
or not the event should be passed on to other widgets.
|
||||
These are called by the default HandleEvent function.
|
||||
*/
|
||||
GUI_status GUI_Widget::KeyDown(const Common::KeyState &key) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
GUI_status GUI_Widget::KeyUp(Common::KeyState key) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
GUI_status GUI_Widget::MouseDown(int x, int y, Events::MouseButton button) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
GUI_status GUI_Widget::MouseUp(int x, int y, Events::MouseButton button) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
|
||||
GUI_status GUI_Widget::MouseMotion(int x, int y, uint8 state) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
GUI_status GUI_Widget::MouseWheel(sint32 x, sint32 y) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
/* Main event handler function.
|
||||
This function gets raw SDL events from the GUI.
|
||||
*/
|
||||
// Idle and HandleEvent produce delayed clicks. Don't override if using those. -- SB-X
|
||||
GUI_status GUI_Widget::HandleEvent(const Common::Event *event) {
|
||||
if (status == WIDGET_HIDDEN) //we don't care for events if we are hidden.
|
||||
return GUI_PASS;
|
||||
|
||||
if (children.empty() == false) {
|
||||
/* handle our children */
|
||||
for (GUI_Widget *child : children) {
|
||||
GUI_status status_ = child->HandleEvent(event);
|
||||
if (status_ != GUI_PASS)
|
||||
return status_;
|
||||
}
|
||||
}
|
||||
|
||||
if (delayed_button != 0 || held_button != 0) {
|
||||
GUI_status status_ = try_mouse_delayed();
|
||||
if (status_ != GUI_PASS)
|
||||
return status_;
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case Common::EVENT_KEYDOWN:
|
||||
return KeyDown(event->kbd.keycode);
|
||||
break;
|
||||
case Common::EVENT_KEYUP:
|
||||
return KeyUp(event->kbd.keycode);
|
||||
break;
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
case Common::EVENT_MBUTTONDOWN: {
|
||||
int x, y;
|
||||
Events::MouseButton button;
|
||||
x = event->mouse.x;
|
||||
y = event->mouse.y;
|
||||
button = Events::whichButton(event->type);
|
||||
if (focused || HitRect(x, y)) {
|
||||
set_mousedown(SDL_GetTicks(), button);
|
||||
return(MouseDown(x, y, button));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
case Common::EVENT_MBUTTONUP: {
|
||||
int x, y;
|
||||
Events::MouseButton button;
|
||||
x = event->mouse.x;
|
||||
y = event->mouse.y;
|
||||
button = Events::whichButton(event->type);
|
||||
if (focused || HitRect(x, y)) {
|
||||
int rel_time = SDL_GetTicks();
|
||||
int last_rel_time = get_mouseup(button);
|
||||
bool do_mouseclick = get_mousedown(button);
|
||||
set_mouseup(rel_time, button);
|
||||
if (do_mouseclick && accept_mouseclick[button - 1] && (rel_time - last_rel_time < GUI::mouseclick_delay)) {
|
||||
// before a Double or Delayed click, mouseup_time is reset so another click isn't possible
|
||||
set_mouseup(0, button);
|
||||
return MouseDouble(x, y, button);
|
||||
} else if (do_mouseclick && accept_mouseclick[button - 1])
|
||||
return MouseClick(x, y, button);
|
||||
else
|
||||
return MouseUp(x, y, button);
|
||||
}
|
||||
/* if widget was clicked before we must let it deactivate itself*/
|
||||
else if (ClickState(1)) {
|
||||
set_mouseup(0, button);
|
||||
return MouseUp(-1, -1, button);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Common::EVENT_MOUSEMOVE: {
|
||||
int x, y;
|
||||
uint8 state;
|
||||
x = event->mouse.x;
|
||||
y = event->mouse.y;
|
||||
state = Events::get()->getButtonState();
|
||||
if (state > 0) // mousemotion resets Click
|
||||
mouse_moved = true;
|
||||
if (focused || HitRect(x, y)) {
|
||||
if (!mouse_over) {
|
||||
mouse_over = true;
|
||||
MouseEnter(state);
|
||||
}
|
||||
return MouseMotion(x, y, state);
|
||||
} else {
|
||||
if (mouse_over) {
|
||||
mouse_over = false;
|
||||
MouseLeave(state);
|
||||
}
|
||||
/* if widget was clicked before we must let it react*/
|
||||
if (ClickState(1))
|
||||
return MouseMotion(-1, -1, state);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Common::EVENT_WHEELUP:
|
||||
return MouseWheel(0, 1);
|
||||
|
||||
case Common::EVENT_WHEELDOWN:
|
||||
return MouseWheel(0, -1);
|
||||
|
||||
default: {
|
||||
/* Pass it along.. */;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
// iterate through children if present to hit the correct drag area.
|
||||
bool GUI_Widget::drag_accept_drop(int x, int y, int message, void *data) {
|
||||
if (children.empty() == false) {
|
||||
for (GUI_Widget *child : children) {
|
||||
if (child->HitRect(x, y)) {
|
||||
if (child->drag_accept_drop(x, y, message, data))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUI::get_gui()->force_full_redraw();
|
||||
return false;
|
||||
}
|
||||
|
||||
void GUI_Widget::drag_perform_drop(int x, int y, int message, void *data) {
|
||||
if (children.empty() == false) {
|
||||
for (GUI_Widget *child : children) {
|
||||
if (child->HitRect(x, y)) {
|
||||
child->drag_perform_drop(x, y, message, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mouse button was pressed and released over the widget.
|
||||
*/
|
||||
GUI_status GUI_Widget::MouseClick(int x, int y, Events::MouseButton button) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
/* Mouse button was clicked twice over the widget, within a certain time period.
|
||||
*/
|
||||
GUI_status GUI_Widget::MouseDouble(int x, int y, Events::MouseButton button) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
/* Mouse cursor passed out of the widget area.
|
||||
*/
|
||||
GUI_status GUI_Widget::MouseEnter(uint8 state) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
/* Mouse cursor passed into the widget area.
|
||||
*/
|
||||
GUI_status GUI_Widget::MouseLeave(uint8 state) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
/* Returns false if any widget but this one is focused or locked.
|
||||
*/
|
||||
bool GUI_Widget::widget_has_focus() {
|
||||
GUI_Widget *focused_widget = GUI::get_gui()->get_focused_widget();
|
||||
GUI_Widget *locked_widget = GUI::get_gui()->get_locked_widget();
|
||||
|
||||
if (GUI::get_gui()->get_block_input())
|
||||
return false;
|
||||
if (locked_widget != nullptr && locked_widget != this)
|
||||
return false;
|
||||
if (focused_widget != nullptr && focused_widget != this)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// button 0 = all
|
||||
void GUI_Widget::set_accept_mouseclick(bool set, int button) {
|
||||
if (button <= 0)
|
||||
accept_mouseclick[0] = accept_mouseclick[1] = accept_mouseclick[2] = set;
|
||||
else if (button < 4)
|
||||
accept_mouseclick[button - 1] = set;
|
||||
set_mouseup(0, button);
|
||||
set_mousedown(0, button);
|
||||
}
|
||||
|
||||
// time 0 = reset; button 0 = all
|
||||
// mousedown is always cleared
|
||||
void GUI_Widget::set_mouseup(int set, int button) {
|
||||
mouse_moved = false;
|
||||
if (button <= 0) {
|
||||
mouseup[0] = mouseup[1] = mouseup[2] = set;
|
||||
mousedown[0] = mousedown[1] = mousedown[2] = 0;
|
||||
} else if (button < 4) {
|
||||
mouseup[button - 1] = set;
|
||||
mousedown[button - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// time 0 = reset; button 0 = all
|
||||
// mouseup is not cleared because two mouseup times are compared for mouseclicks
|
||||
void GUI_Widget::set_mousedown(int set, int button) {
|
||||
if (button <= 0) {
|
||||
// mouseup[0]=mouseup[1]=mouseup[2] = 0;
|
||||
mousedown[0] = mousedown[1] = mousedown[2] = set;
|
||||
} else if (button < 4) {
|
||||
// mouseup[button-1] = 0;
|
||||
mousedown[button - 1] = set;
|
||||
}
|
||||
}
|
||||
|
||||
// check to see if time has passed for a MouseDelayed or MouseHeld
|
||||
GUI_status GUI_Widget::try_mouse_delayed() {
|
||||
int mousedown_time = get_mousedown(held_button);
|
||||
int mouseup_time = get_mouseup(delayed_button);
|
||||
int time_to_hold = SDL_GetTicks() - mousedown_time;
|
||||
int time_to_click = SDL_GetTicks() - mouseup_time;
|
||||
|
||||
if (mousedown_time != 0 && time_to_hold >= GUI::mouseclick_delay) {
|
||||
Events::MouseButton button = held_button;
|
||||
int x, y; // position isn't saved anywhere so we get it here
|
||||
screen->get_mouse_location(&x, &y); // hopefully it hasn't changed since MouseDown
|
||||
held_button = Events::BUTTON_NONE; // no need to clear mousedown time, MouseUp does that
|
||||
return (MouseHeld(x, y, button));
|
||||
}
|
||||
|
||||
if (mouseup_time != 0 && time_to_click >= GUI::mouseclick_delay) {
|
||||
Events::MouseButton button = delayed_button;
|
||||
int x, y; // position isn't saved anywhere so we get it here
|
||||
screen->get_mouse_location(&x, &y); // hopefully it hasn't changed since MouseClick/MouseUp
|
||||
delayed_button = Events::BUTTON_NONE;
|
||||
// before a Double or Delayed click, mouseup time is reset
|
||||
set_mouseup(0, button);
|
||||
return (MouseDelayed(x, y, button));
|
||||
}
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
// like a MouseClick but called only after waiting for MouseDouble, if
|
||||
// wait_for_mouseclick(button) was called
|
||||
GUI_status GUI_Widget::MouseDelayed(int x, int y, Events::MouseButton button) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
// like a MouseDown but called only after waiting for MouseUp, if
|
||||
// wait_for_mousedown(button) was called
|
||||
GUI_status GUI_Widget::MouseHeld(int x, int y, Events::MouseButton button) {
|
||||
return GUI_PASS;
|
||||
}
|
||||
|
||||
} // End of namespace Nuvie
|
||||
} // End of namespace Ultima
|
||||
Reference in New Issue
Block a user