mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
87882c9976
Isolate ui from rest of the project, minor cleanups.
422 lines
10 KiB
C++
422 lines
10 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* 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/>.
|
|
|
|
--- HISTORICAL COMMENTS FOLLOW ---
|
|
|
|
* $Logfile: /DescentIII/Main/ui/UIGadget.cpp $
|
|
* $Revision: 28 $
|
|
* $Date: 10/21/99 3:31p $
|
|
* $Author: Kevin $
|
|
*
|
|
* Gadget code
|
|
*
|
|
* $Log: /DescentIII/Main/ui/UIGadget.cpp $
|
|
*
|
|
* 28 10/21/99 3:31p Kevin
|
|
* mac merge & fix for slider
|
|
*
|
|
* 27 4/14/99 1:52a Jeff
|
|
* fixed case mismatched #includes
|
|
*
|
|
* 26 3/23/99 9:02p Samir
|
|
* always null hotkey when creating gadget!
|
|
*
|
|
* 25 3/01/99 7:29p Samir
|
|
* fixed an issue with slaves getting freed after their master was
|
|
* destroyed for sliders.
|
|
*
|
|
* 24 2/22/99 8:13p Samir
|
|
* added slave gadget system that only uses mouse input from window, but
|
|
* defers keys to its master.
|
|
*
|
|
* 23 2/21/99 6:36p Samir
|
|
* focusing changes and key input changes to ui.,
|
|
*
|
|
* 22 2/17/99 8:32p Samir
|
|
* fixed bugs in remove gadget, added callbacks for attaching and
|
|
* detaching gadgets.
|
|
*
|
|
* 21 2/16/99 11:58a Samir
|
|
* small fixes and added gadget notification.
|
|
*
|
|
* 20 10/19/98 12:18p Samir
|
|
* made tabbing between edit controls possible.
|
|
*
|
|
* 19 10/13/98 8:23p Samir
|
|
* communication between gadget and window and edit system to work
|
|
* together.
|
|
*
|
|
* 18 8/26/98 12:25p Samir
|
|
* fixed the whole editbox, keyboard access mess.
|
|
*
|
|
* 17 8/25/98 7:08p Samir
|
|
* select how keys are sent to a gadget.
|
|
*
|
|
* 16 6/22/98 7:30p Samir
|
|
* set focus on gadget when calling LockFocus.
|
|
*
|
|
* 15 6/05/98 5:35p Samir
|
|
* massive improvement in UI keyboard interface.
|
|
*
|
|
* 14 5/25/98 8:18p Samir
|
|
* Enter keys are passed to gadgets too.
|
|
*
|
|
* 13 4/24/98 2:43a Samir
|
|
* added IsLocked function.
|
|
*
|
|
* 12 3/24/98 4:26p Samir
|
|
* fixed minor bug in TrapKey.
|
|
*
|
|
* 11 3/10/98 7:24p Samir
|
|
* Save original width and height.
|
|
*
|
|
* 10 3/09/98 2:57p Samir
|
|
* Remove gadget from window called from gadget destroy.
|
|
*
|
|
* 9 2/15/98 7:07p Samir
|
|
* Revamped way keys like up and down are handled in window system.
|
|
*
|
|
* 8 2/13/98 6:35p Samir
|
|
* Fixed some gadget creation/destruction inconsistancies
|
|
*
|
|
* 7 1/30/98 7:05p Samir
|
|
* Each gadget now has their own coordinate system.
|
|
*
|
|
* 6 1/23/98 5:34p Samir
|
|
* Allow for nonpersistant UIItems.
|
|
*
|
|
* 5 1/18/98 4:22p Samir
|
|
* Implemented new UIItem system.
|
|
*
|
|
* 4 1/13/98 4:27p Samir
|
|
* Keyboard timing delay stored UIGadget. Moved from UIEdit.
|
|
*
|
|
* 3 1/05/98 4:36p Samir
|
|
* Moved centering and fittext control flags to UIGadget class.
|
|
*
|
|
* 2 1/02/98 12:43p Samir
|
|
* Gadget processing functional.
|
|
*
|
|
* 1 12/30/97 4:36p Samir
|
|
* Initial revision
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include <cstring>
|
|
|
|
#include "UIlib.h"
|
|
#include "Macros.h"
|
|
|
|
int UIGadget::m_LastKey = 0;
|
|
int UIGadget::m_LastKeyCount = 0;
|
|
float UIGadget::m_LastKeyTime = 0.0f;
|
|
|
|
UIGadget::UIGadget() : UIObject() {
|
|
m_ID = 0;
|
|
m_Wnd = NULL;
|
|
m_Prev = m_PrevSlave = NULL;
|
|
m_Next = m_NextSlave = NULL;
|
|
m_MasterGadget = NULL;
|
|
m_SlaveGadgets = NULL;
|
|
m_CurrentSlave = NULL;
|
|
m_infocus = false;
|
|
}
|
|
|
|
UIGadget::~UIGadget() {}
|
|
|
|
// ALL Gadgets must have a parent.
|
|
void UIGadget::Create(UIWindow *wnd, int id, int x, int y, int w, int h, int flags) {
|
|
ASSERT(!IsCreated());
|
|
ASSERT(wnd);
|
|
|
|
m_ID = id;
|
|
m_Wnd = wnd;
|
|
|
|
m_Disabled = false;
|
|
m_Flags = flags;
|
|
m_infocus = false;
|
|
|
|
// add to interface's list.
|
|
UIObject::Create(x, y, w, h);
|
|
|
|
// save original width and height
|
|
m_SavedW = m_W;
|
|
m_SavedH = m_H;
|
|
m_Hotkey = 0;
|
|
|
|
// handle formatting issues.
|
|
OnFormat();
|
|
m_MasterGadget = NULL;
|
|
m_SlaveGadgets = NULL;
|
|
m_CurrentSlave = NULL;
|
|
m_NextSlave = m_PrevSlave = NULL;
|
|
m_Datum = 0;
|
|
|
|
m_Wnd->AddGadget(this);
|
|
}
|
|
|
|
void UIGadget::Destroy() {
|
|
// slaves are freed or destroyed by now.
|
|
m_SlaveGadgets = NULL;
|
|
|
|
// remove gadget from windw list.
|
|
if (m_Wnd) {
|
|
m_Wnd->RemoveGadget(this);
|
|
}
|
|
OnDestroy();
|
|
UIObject::Destroy();
|
|
}
|
|
|
|
// sets hotkey for gadget
|
|
void UIGadget::SetHotkey(int key) { m_Hotkey = key; }
|
|
|
|
void UIGadget::SetFlag(int flag) { m_Flags |= flag; }
|
|
|
|
void UIGadget::ClearFlag(int flag) { m_Flags &= (~flag); }
|
|
|
|
// locks input on this gadget
|
|
void UIGadget::LockFocus() {
|
|
// m_Wnd->SetFocusOnGadget(this);
|
|
m_Wnd->LockFocusOnGadget(this);
|
|
}
|
|
|
|
void UIGadget::UnlockFocus() { m_Wnd->UnlockFocusOnGadget(); }
|
|
|
|
// called before drawing usually. sets x,y,w,h optionally.
|
|
void UIGadget::OnFormat() {
|
|
if (m_Flags & UIF_CENTER)
|
|
m_X = m_Wnd->W() / 2 - m_W / 2;
|
|
}
|
|
|
|
// interprets input on this gadget. this gadget is in focus.
|
|
void UIGadget::Process(bool do_keys, bool do_mouse, bool do_user) {
|
|
// do mouse processing on this gadget if and only if no slave processing was done.
|
|
if (UI_input.b1_status == UIMSEBTN_PRESSED && do_mouse) {
|
|
OnMouseBtnDown(UILMSEBTN);
|
|
UI_input.b1_last_status = UIMSEBTN_PRESSED; // DAJ use to latch the UI mouse button
|
|
} else if (UI_input.b1_status == UIMSEBTN_RELEASED && do_mouse) {
|
|
OnMouseBtnUp(UILMSEBTN);
|
|
UI_input.b1_last_status = UIMSEBTN_RELEASED; // DAJ use to latch the UI mouse button
|
|
} else if (UI_input.b1_status == UIMSEBTN_CLICKED && do_mouse) {
|
|
OnMouseBtnDown(UILMSEBTN);
|
|
OnMouseBtnUp(UILMSEBTN);
|
|
} else if (do_keys) {
|
|
// keyboard processing.
|
|
switch (UI_input.key) {
|
|
case KEY_ENTER:
|
|
if ((UI_input.key_status == UIKEY_PRESSED) || (UI_input.key_status == UIKEY_CLICKED)) {
|
|
OnSelect();
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (UI_input.key_status == UIKEY_PRESSED) {
|
|
if (TrapKey(UI_input.key))
|
|
OnKeyDown(UI_input.key);
|
|
} else if (UI_input.key_status == UIKEY_RELEASED) {
|
|
UntrapKey(UI_input.key);
|
|
OnKeyUp(UI_input.key);
|
|
} else if (UI_input.key_status == UIKEY_CLICKED) {
|
|
if (TrapKey(UI_input.key))
|
|
OnKeyDown(UI_input.key);
|
|
UntrapKey(UI_input.key);
|
|
OnKeyUp(UI_input.key);
|
|
}
|
|
}
|
|
|
|
// run gadget's user process
|
|
if (do_user) {
|
|
OnUserProcess();
|
|
}
|
|
}
|
|
|
|
// when gadget is selected, this function will AT LEAST be called.
|
|
// typically this function is called by a child class when we want
|
|
// to tell the caller that this gadget was selected for something.
|
|
void UIGadget::OnSelect() {
|
|
if (m_MasterGadget && CHECK_FLAG(m_Flags, UIF_NOTIFYMASTERSEL)) {
|
|
m_MasterGadget->OnNotifySelect(this);
|
|
} else {
|
|
UI_output.id = m_ID;
|
|
UI_output.gadget = this;
|
|
}
|
|
}
|
|
|
|
void UIGadget::AttachSlaveGadget(UIGadget *gadget) {
|
|
// attach to list.
|
|
ASSERT(gadget);
|
|
ASSERT(!m_MasterGadget); // slaves can't own each other (sorry, been reading Uncle Tom's Cabin in class)
|
|
|
|
gadget->m_Flags |= UIF_SLAVE;
|
|
gadget->m_MasterGadget = this;
|
|
if (m_SlaveGadgets) {
|
|
m_SlaveGadgets->m_NextSlave = gadget;
|
|
}
|
|
gadget->m_PrevSlave = m_SlaveGadgets;
|
|
gadget->m_NextSlave = NULL;
|
|
m_SlaveGadgets = gadget;
|
|
}
|
|
|
|
void UIGadget::DetachSlaveGadget(UIGadget *gadget) {
|
|
if (!gadget) {
|
|
return;
|
|
}
|
|
|
|
ASSERT(gadget->m_MasterGadget == this);
|
|
ASSERT(!m_MasterGadget); // slaves can't own each other (sorry, been reading Uncle Tom's Cabin in class)
|
|
|
|
if (gadget == m_SlaveGadgets) {
|
|
m_SlaveGadgets = gadget->m_PrevSlave;
|
|
}
|
|
if (gadget == m_CurrentSlave) {
|
|
m_CurrentSlave->LostFocus();
|
|
m_CurrentSlave = gadget->m_PrevSlave ? gadget->m_PrevSlave : gadget->m_NextSlave;
|
|
}
|
|
if (gadget->m_PrevSlave) {
|
|
gadget->m_PrevSlave->m_NextSlave = gadget->m_NextSlave;
|
|
}
|
|
if (gadget->m_NextSlave) {
|
|
gadget->m_NextSlave->m_PrevSlave = gadget->m_PrevSlave;
|
|
}
|
|
|
|
gadget->m_MasterGadget = NULL;
|
|
gadget->m_Flags &= (~UIF_SLAVE);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// PRIVATE FUNCTIONS
|
|
|
|
// called by window system
|
|
void UIGadget::LostFocus() {
|
|
this->OnLostFocus(); // should invoke gadget's OnLostFocus function
|
|
if (m_CurrentSlave) {
|
|
ASSERT(m_SlaveGadgets);
|
|
m_CurrentSlave->LostFocus();
|
|
m_CurrentSlave = NULL;
|
|
}
|
|
m_infocus = false;
|
|
}
|
|
|
|
// called by window system
|
|
void UIGadget::GainFocus() {
|
|
// m_LastKey = 0;
|
|
// m_LastKeyTime = 0.0f;
|
|
|
|
m_infocus = true;
|
|
m_CurrentSlave = NULL;
|
|
this->OnGainFocus(); // should invoke gadget's OnGainFocus function
|
|
}
|
|
|
|
void UIGadget::CheckFocusOnSlaves(int mx, int my) {
|
|
UIGadget *slave = m_SlaveGadgets;
|
|
|
|
if (CHECK_FLAG(m_Flags, UIF_SLAVE))
|
|
return;
|
|
|
|
while (slave) {
|
|
if (PT_IN_GADGET(m_Wnd, slave, mx, my)) {
|
|
if (m_CurrentSlave) {
|
|
m_CurrentSlave->LostFocus();
|
|
}
|
|
m_CurrentSlave = slave;
|
|
m_CurrentSlave->GainFocus();
|
|
break;
|
|
}
|
|
slave = slave->m_PrevSlave;
|
|
}
|
|
|
|
if (!slave) {
|
|
if (m_CurrentSlave) {
|
|
m_CurrentSlave->LostFocus();
|
|
m_CurrentSlave = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// used to manage keypresses so repeating isn't quick.
|
|
bool UIGadget::TrapKey(int key) {
|
|
float time;
|
|
time = timer_GetTime();
|
|
|
|
// take care of multiple keypresses and key down repeats. handle timing
|
|
// so it feels 'natural'
|
|
if (m_LastKey && m_LastKey == key) {
|
|
if (m_LastKeyCount == 1) {
|
|
if ((time - m_LastKeyTime) < KEYDOWN_FIRST_DELAY)
|
|
return false;
|
|
} else if ((time - m_LastKeyTime) < KEYDOWN_REPEAT_DELAY) {
|
|
return false;
|
|
}
|
|
m_LastKeyCount++;
|
|
} else {
|
|
m_LastKeyCount = 1;
|
|
}
|
|
|
|
m_LastKey = key;
|
|
m_LastKeyTime = time;
|
|
|
|
return true;
|
|
}
|
|
|
|
void UIGadget::UntrapKey(int key) {
|
|
// if the key previously pressed went up, then
|
|
// turn off key repeat management.
|
|
if (key == m_LastKey) {
|
|
m_LastKeyCount = 0;
|
|
m_LastKey = 0;
|
|
}
|
|
}
|
|
|
|
// is this gadget's focus locked?
|
|
bool UIGadget::IsLocked() const {
|
|
if (m_Wnd->m_LockedGadget == this)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
UIGadget *UIGadget::GetPrevUsableGadget() {
|
|
UIGadget *gadget = this;
|
|
UIGadget *next_gadget = NULL;
|
|
|
|
while (gadget->m_Prev) {
|
|
gadget = gadget->m_Prev;
|
|
if (!gadget->IsDisabled()) {
|
|
next_gadget = gadget;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return next_gadget;
|
|
}
|
|
|
|
UIGadget *UIGadget::GetNextUsableGadget() {
|
|
UIGadget *gadget = this;
|
|
UIGadget *next_gadget = NULL;
|
|
|
|
while (gadget->m_Next) {
|
|
gadget = gadget->m_Next;
|
|
if (!gadget->IsDisabled()) {
|
|
next_gadget = gadget;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return next_gadget;
|
|
}
|