CustomGUIs: major rework of layouting & controls; better integration of C4GUI

Controls
David Dormagen 2014-10-11 11:29:02 +02:00
parent 7319f7b3cc
commit 049088be78
11 changed files with 541 additions and 305 deletions

View File

@ -233,7 +233,7 @@ func CreateNewInventoryButton(int max_slots)
ID = 1000 + slot_info.ID
}
};
CustomGuiUpdate({new_icon = icon}, GetInventoryGuiID(), 0);
CustomGuiUpdate({_new_icon = icon}, GetInventoryGuiID(), 0);
//return bt;
}

View File

@ -108,7 +108,7 @@ func AddItem(symbol, string text, user_ID, proplist target, command, parameter,
custom_entry = MakeEntryProplist(symbol, text, ID, on_hover, on_hover_stop);
entries[ID - 1] = [target, command, parameter, user_ID];
this[Format("menuChild%d", ID)] = custom_entry;
this[Format("_menuChild%d", ID)] = custom_entry;
// need to add to existing menu?
if (custom_menu_id)
@ -120,7 +120,7 @@ func AddItem(symbol, string text, user_ID, proplist target, command, parameter,
CustomGuiClose(custom_menu_id, ID, this);
}
var temp = {child = custom_entry};
var temp = {_child = custom_entry};
CustomGuiUpdate(temp, custom_menu_id, this.ID, this);
}

View File

@ -61,7 +61,8 @@ global func Gui_AddSubwindow(proplist submenu, proplist menu)
{
do
{
var uniqueID = Format("child%d", RandomX(10000, 0xffffff));
// use an anonymous name starting with an underscore
var uniqueID = Format("_child%d", RandomX(10000, 0xffffff));
if (menu[uniqueID] != nil) continue;
menu[uniqueID] = submenu;
return true;

View File

@ -31,7 +31,10 @@ func MainOnHover(parameter, int ID)
func StartMenu(plr)
{
if (active_menu)
CustomGuiClose(active_menu);
{
if (CustomGuiClose(active_menu)) return;
}
var main_menu =
{
Decoration = GUI_MenuDeco,
@ -153,7 +156,13 @@ func StartScenarioOptionsTest(parameter, int ID, int player)
{
ID = 1,
Target = scenoptions_dummies[0],
Text = "Please select the scenario options!"
// this is also a test for updating children by name
icon = {Left="50%-4em", Right="50%+4em", Bottom="5em", Top="1em", Symbol=Clonk},
textwindow =
{
Top = "6em",
Text = "Please select the scenario options!"
}
},
clientdesc =
{
@ -178,6 +187,7 @@ func StartScenarioOptionsTest(parameter, int ID, int player)
{
ID = rule.ID,
Bottom = "+4em",
Tooltip = rule.def->GetName(),
icon = {Priority = 10, Symbol = rule.def, Right = "+4em", Bottom = "+4em"},
text = {Priority = 10, Left = "+4em", Style = GUI_TextVCenter, Text = rule.def.Name},
@ -227,7 +237,12 @@ func ScenOptsUpdateDesc(data, int player, int ID, int subwindowID, object target
var text = "<c ff0000>Do you really want to remove the rule???</c>";
if (!data[2])
text = data[0].Description;
Gui_UpdateText(text, active_menu, 1, scenoptions_dummies[0]);
var update =
{
icon = {Symbol = data[0]},
textwindow = {Text = text}
};
CustomGuiUpdate(update, active_menu, 1, scenoptions_dummies[0]);
}
/* ------------------------ player list test ----------------------------- */

View File

@ -1485,13 +1485,7 @@ void C4Game::Default()
DebugPassword.Clear();
DebugHost.Clear();
DebugWait = false;
// pGui is empty atm. Readd script window to GUI.
// the window will be deleted by pGui->Clear
const float standardVerticalBorder = 100.0f;
const float standardHorizontalBorder = 100.0f;
GuiWindowRoot = new C4GuiWindow(standardVerticalBorder, standardHorizontalBorder);
pGUI->AddElement(GuiWindowRoot);
GuiWindowRoot = nullptr; // will be initialized when the game starts
}
void C4Game::Evaluate()
@ -2252,6 +2246,14 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4Value
SetMusicLevel(iMusicLevel);
SetInitProgress(97);
}
// prepare script menus
assert(!GuiWindowRoot);
const float standardVerticalBorder = 100.0f;
const float standardHorizontalBorder = 100.0f;
GuiWindowRoot = new C4GuiWindow(standardVerticalBorder, standardHorizontalBorder);
pGUI->AddElement(GuiWindowRoot);
return true;
}

View File

@ -910,6 +910,7 @@ namespace C4GUI
void SetScrollPos(int32_t iToPos) { iScrollPos = iToPos * GetMaxScroll() / (iCBMaxRange-1); }
friend class ScrollWindow;
friend class C4GuiWindow;
};
// a window that can be scrolled
@ -2492,7 +2493,6 @@ namespace C4GUI
{
public:
int32_t x,y; // cursor position
int32_t forPlayer; // player that the mouse movement originated from
bool LDown, MDown, RDown; // mouse button states
int32_t LDownX, LDownY; // position where left button was pressed last
DWORD dwKeys; // shift, ctrl, etc.

View File

@ -126,6 +126,10 @@ namespace C4GUI
if (pLast) pLast->pNext = pChild; else pFirst = pChild;
pChild->pPrev = pLast; pChild->pNext = NULL; pLast = pChild;
pChild->pParent = this;
assert(pChild->pNext != pChild);
assert(pChild->pPrev != pChild);
assert(pChild->pParent != pChild);
}
void Container::ReaddElement(Element *pChild)
@ -138,6 +142,10 @@ namespace C4GUI
// add to end of list
if (pLast) pLast->pNext = pChild; else pFirst = pChild;
pChild->pPrev = pLast; pChild->pNext = NULL; pLast = pChild;
assert(pChild->pNext != pChild);
assert(pChild->pPrev != pChild);
assert(pChild->pParent != pChild);
}
void Container::InsertElement(Element *pChild, Element *pInsertBefore)
@ -155,6 +163,10 @@ namespace C4GUI
pFirst = pChild;
pChild->pNext = pInsertBefore; pInsertBefore->pPrev = pChild;
pChild->pParent = this;
assert(pChild->pNext != pChild);
assert(pChild->pPrev != pChild);
assert(pChild->pParent != pChild);
}
Element *Container::GetNextNestedElement(Element *pPrevElement, bool fBackwards)
@ -503,8 +515,10 @@ namespace C4GUI
pScrollBar = new ScrollBar(rtBounds, this);
// add self and scroll bar to window
if (pParentWindow != this)
{
pParentWindow->AddElement(this);
pParentWindow->AddElement(pScrollBar);
pParentWindow->AddElement(pScrollBar);
}
}
void ScrollWindow::Update()

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,7 @@ enum C4GuiWindowPropertyName
style,
priority,
player,
tooltip,
_lastProp
};
@ -172,35 +173,26 @@ class C4GuiWindowProperty
void ClearPointers(C4Object *pObj);
};
class C4GuiWindowScrollBar
{
friend class C4GuiWindow;
public:
float offset;
C4GuiWindowScrollBar();
virtual ~C4GuiWindowScrollBar();
void ScrollBy(float val) { offset += val; if (offset < 0.0f) offset = 0.0f; else if (offset > 1.0f) offset = 1.0f; }
void Draw(C4TargetFacet &cgo, int32_t player, float parentLeft, float parentTop, float parentRight, float parentBottom);
virtual bool MouseInput(int32_t button, int32_t mouseX, int32_t mouseY, DWORD dwKeyParam);
private:
C4GUI::ScrollBarFacets *decoration;
C4GuiWindow *parent;
};
class C4GuiWindow : public C4GUI::ScrollWindow
{
friend class C4GuiWindowAction;
friend class C4GuiWindowScrollBar;
private:
// the menu ID is always unique, however the sub-menu IDs do NOT have to be unique
// the "main" menu ID is always unique, however the sub-menu IDs do NOT have to be unique
// they can be set from script and in combination with the target should suffice to identify windows
int32_t id;
// The name of a window is used when updating a window to identify the correct child;
// however, it is NOT generally used to identify windows, f.e. to close them.
// The reasoning behind that is that EVERY window needs a name (in the defining proplist) but only windows that need to be addressed later need an ID;
// so separating the identifying property from the name hopefully reduces clashes - especially since names will often be "left"/"right" etc.
// Note that a window does not necessarily have a name and names starting with an underscore are never saved (to be able to f.e. add new windows without knowing the names of the existing windows).
C4String *name;
// this is not only a window inside a menu but a top-level-window?
// this does not mean the ::WindowMenuRoot but rather a player-created submenu
bool isMainWindow;
// whether this menu is the root of all script-created menus (aka of the isMainWindow windows)
bool IsRoot();
bool mainWindowNeedsLayoutUpdate;
void RequestLayoutUpdate();
@ -208,7 +200,6 @@ class C4GuiWindow : public C4GUI::ScrollWindow
bool closeActionWasExecuted; // to prevent a window from calling the close-callback twice even if f.e. closed in the close-callback..
C4Object *target;
const C4Object *GetTarget() { return target; }
C4GuiWindowScrollBar *scrollBar;
// properties are stored extra to make "tags" possible
C4GuiWindowProperty props[C4GuiWindowPropertyName::_lastProp];
@ -224,8 +215,10 @@ class C4GuiWindow : public C4GUI::ScrollWindow
void ChildChangedPriority(C4GuiWindow *child);
// helper function to extract relative and absolute position values from a string
void SetPositionStringProperties(const C4Value &property, C4GuiWindowPropertyName relative, C4GuiWindowPropertyName absolute, C4String *tag);
C4Value PositionToC4Value(C4GuiWindowPropertyName relative, C4GuiWindowPropertyName absolute);
// sets all margins either from a string or from an array
void SetMarginProperties(const C4Value &property, C4String *tag);
C4Value MarginsToC4Value();
// this is only supposed to be called at ::Game.GuiWindowRoot since it uses the "ID" property
// this is done to make saving easier. Since IDs do not need to be sequential, action&menu IDs can both be derived from "id"
@ -258,6 +251,8 @@ class C4GuiWindow : public C4GUI::ScrollWindow
int32_t GetID() { return id; }
// finds a child with a certain ID, usually called on ::MainWindowRoot to get submenus
C4GuiWindow *GetChildByID(int32_t child);
// finds a child by name, usually called when updating a window with a new proplist
C4GuiWindow *GetChildByName(C4String *childName);
// finds any fitting sub menu - not necessarily direct child
// has to be called on children of ::MainWindowRoot, uses the childrenIDMap
// note: always checks the target to avoid ambiguities, even if 0
@ -289,10 +284,13 @@ class C4GuiWindow : public C4GUI::ScrollWindow
// special layouts that are set by styles
void UpdateLayoutGrid();
void UpdateLayoutVertical();
// the window will be drawn in the context of a viewport BY the viewport
// so just do nothing when TheScreen wants to draw the window
virtual void Draw(C4TargetFacet &cgo) {}
// Draw without parameters can be used for the root
bool DrawAll(C4TargetFacet &cgo, int32_t player);
bool Draw(C4TargetFacet &cgo, int32_t player);
// the clipping rectangle has already been set, but currentClippingRect must be passed to DrawChildren
bool Draw(C4TargetFacet &cgo, int32_t player, C4Rect *currentClippingRect);
bool GetClippingRect(int32_t &left, int32_t &top, int32_t &right, int32_t &bottom);
// withMultipleFlag is there to draw only the non-multiple or the multiple windows
@ -300,7 +298,7 @@ class C4GuiWindow : public C4GUI::ScrollWindow
// withMultipleFlag == 0: only one non-Multiple window is drawn
// withMultipleFlag == 1: only Multiple windows are drawn
// returns whether at least one child was drawn
bool DrawChildren(C4TargetFacet &cgo, int32_t player, int32_t withMultipleFlag = -1);
bool DrawChildren(C4TargetFacet &cgo, int32_t player, int32_t withMultipleFlag = -1, C4Rect *currentClippingRect = nullptr);
// used for commands that have been synchronized and are coming from the command queue
// attention: calls to this need to be synchronized!
@ -308,13 +306,13 @@ class C4GuiWindow : public C4GUI::ScrollWindow
// virtual bool MouseInput(int32_t player, int32_t button, int32_t mouseX, int32_t mouseY, DWORD dwKeyParam);
// this is called only on the root menu
virtual void MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam);
virtual bool MouseInput(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam);
// this is then called on the child windows, note the return value
virtual bool ProcessMouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam);
virtual bool ProcessMouseInput(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, int32_t parentOffsetX, int32_t parentOffsetY);
// called when mouse cursor enters element region
virtual void MouseEnter(C4GUI::CMouse &rMouse);
virtual void MouseEnter();
// called when mouse cursor leaves element region
virtual void MouseLeave(C4GUI::CMouse &rMouse);
virtual void MouseLeave();
// this remembers whether the window currently has mouse focus and whether it has been mouse-down-ed
// all windows with mouse focus set are remembered by their parents and notified when the mouse left
enum MouseState // values of this enum will be bit-wise combined
@ -327,7 +325,8 @@ class C4GuiWindow : public C4GUI::ScrollWindow
// OnMouseOut() called by this window, unsets the mouse focus
// must notify children, too!
void OnMouseOut(int32_t player);
void OnMouseIn(int32_t player); // called by this window, sets the mouse focus
// called by this window, sets the mouse focus; the offset is used to set the correct tooltip rectangle for ::MouseControl
void OnMouseIn(int32_t player, int32_t parentOffsetX, int32_t parentOffsetY);
bool HasMouseFocus() { return currentMouseState & MouseState::Focus; }
private:
// TODO: actually scale with font size (needs font to be able to scale first..)

View File

@ -55,7 +55,7 @@ const int32_t C4MC_Cursor_Select = 0, // click cursor to select/click stuf
C4MC_Cursor_DownRight = 10,
C4MC_Cursor_Passive = 11; // passive cursor in records and and fog of war and outside viewport
const int32_t C4MC_Time_on_Target = 10;
const int32_t C4MC_Tooltip_Delay = 20;
C4MouseControl::C4MouseControl()
{
@ -92,7 +92,6 @@ void C4MouseControl::Default()
Drag=C4MC_Drag_None;
Selection.Default();
TargetObject=DownTarget=NULL;
TimeOnTargetObject=0;
ControlDown=false;
ShiftDown=false;
AltDown=false;
@ -321,7 +320,10 @@ void C4MouseControl::Move(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyFl
bool menuProcessed = false;
if (pPlayer)
// adjust by viewport X/Y because the GUI windows calculate their positions (and thus check input) based on that
menuProcessed = ::Game.GuiWindowRoot->MouseInput(Player, iButton, iX + Viewport->DrawX, iY + Viewport->DrawY, dwKeyFlags);
menuProcessed = ::Game.GuiWindowRoot->MouseInput(iButton, iX, iY, dwKeyFlags);
if (menuProcessed)
Cursor = C4MC_Cursor_Select;
// if not caught by a menu
if (!menuProcessed)
@ -527,31 +529,46 @@ void C4MouseControl::UpdateCursorTarget()
if (IsPassive())
Cursor=C4MC_Cursor_Passive;
// Time on target: caption
if (TargetObject && Cursor == C4MC_Cursor_Select)
// update tooltip information
if (OldTargetObject != TargetObject)
{
TimeOnTargetObject++;
if (TimeOnTargetObject>=C4MC_Time_on_Target)
if (TargetObject && (Cursor == C4MC_Cursor_Select) && (TargetObject->Category & C4D_MouseSelect))
{
if (TargetObject->Category & C4D_MouseSelect)
{
// set caption
if (!KeepCaption)
{
C4String *tooltip = TargetObject->GetPropertyStr(P_Tooltip);
if(tooltip) {
Caption = TargetObject->GetPropertyStr(P_Tooltip)->GetData();
}
}
}
float objX, objY;
TargetObject->GetViewPos(objX, objY, -fctViewportGUI.X, -fctViewportGUI.Y, fctViewportGUI);
objX += TargetObject->Shape.x;
objY += TargetObject->Shape.y - TargetObject->addtop();
SetTooltipRectangle(C4Rect(objX, objY, TargetObject->Shape.Wdt, TargetObject->Shape.Hgt + TargetObject->addtop()));
SetTooltipText(StdStrBuf(TargetObject->GetPropertyStr(P_Tooltip)->GetCStr()));
}
else
{
SetTooltipRectangle(C4Rect(0, 0, 0, 0));
}
}
if (!KeepCaption
&& ToolTipRectangle.Wdt != 0
&& Inside(GuiX, ToolTipRectangle.x, ToolTipRectangle.x + ToolTipRectangle.Wdt)
&& Inside(GuiY, ToolTipRectangle.y, ToolTipRectangle.y + ToolTipRectangle.Hgt))
{
++TimeInTooltipRectangle;
if (TimeInTooltipRectangle >= C4MC_Tooltip_Delay)
{
Caption = TooltipText;
}
}
else
TimeOnTargetObject=0;
{
// disable tooltip pop-up; whatever set it in the first place will set it again on the next mouse-enter
TimeInTooltipRectangle = 0;
ToolTipRectangle.Wdt = 0;
}
}
// Make a script callback if the object being hovered changes
if(OldTargetObject != TargetObject)
if (OldTargetObject != TargetObject)
{
// TODO: This might put a heavy load on the network, depending on the number of
// selectable objects around. If it turns out to be a problem we might want to
@ -885,6 +902,20 @@ const char *C4MouseControl::GetCaption()
return Caption.getData();
}
void C4MouseControl::SetTooltipRectangle(const C4Rect &rectangle)
{
// Set the tooltip rectangle slightly larger than originally requested.
// The tooltip will be removed when the cursor leaves the rectangle, so make sure that the tooltip is not already disabled when the cursor moves to the border-pixel of the GUI item
// in case the GUI item uses a different check for bounds (< vs <=) than the tooltip rectangle.
ToolTipRectangle = C4Rect(rectangle.x - 2, rectangle.y - 2, rectangle.Wdt + 4, rectangle.Hgt + 4);
TimeInTooltipRectangle = 0;
}
void C4MouseControl::SetTooltipText(const StdStrBuf &text)
{
TooltipText = text;
}
C4Object *C4MouseControl::GetTargetObject()
{
// find object

View File

@ -55,10 +55,6 @@ protected:
int32_t Cursor;
StdCopyStrBuf Caption;
int32_t CaptionBottomY;
int32_t KeepCaption;
int32_t VpX,VpY; // Pixel coordinates of mouse pos
float ViewX,ViewY; // Game coordinate scrolling offset of viewport
float GameX,GameY; // Game coordinates of mouse pos
@ -86,17 +82,26 @@ protected:
C4ID DragID;
C4Def* DragImageDef;
C4Object* DragImageObject;
// Tooltip management
// currently shown caption
StdCopyStrBuf Caption;
// tooltip text that will be shown when the mouse is kept in the tooltip rectangle for some time
StdCopyStrBuf TooltipText;
int32_t CaptionBottomY;
int32_t KeepCaption;
int32_t TimeInTooltipRectangle;
C4Rect ToolTipRectangle;
// Target object
C4Object *TargetObject; // valid during Move()
C4Object *DownTarget;
int32_t TimeOnTargetObject;
public:
void Default();
void Clear();
bool Init(int32_t iPlayer);
void Execute();
const char *GetCaption();
void HideCursor();
void ShowCursor();
void Draw(C4TargetFacet &cgo, const ZoomData &GameZoom);
@ -109,6 +114,10 @@ public:
bool IsMouseOwned() { return fMouseOwned; }
bool IsActive() { return !!Active; }
bool GetLastGUIPos(int32_t *x_out, int32_t *y_out) const;
const char *GetCaption();
void SetTooltipText(const StdStrBuf &text);
void SetTooltipRectangle(const C4Rect &rectangle);
protected:
void SendPlayerSelectNext();
void UpdateFogOfWar();