1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2023, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18#pragma once
19
20#include "C4Gui.h"
21
22namespace C4GUI
23{
24// a vertical list of elements
25class ListBox : public Control
26{
27private:
28 class C4KeyBinding *pKeyContext, *pKeyUp, *pKeyDown, *pKeyPageUp, *pKeyPageDown, *pKeyHome, *pKeyEnd, *pKeyActivate, *pKeyLeft, *pKeyRight;
29
30 bool KeyContext();
31 bool KeyUp();
32 bool KeyDown();
33 bool KeyLeft();
34 bool KeyRight();
35 bool KeyPageUp();
36 bool KeyPageDown();
37 bool KeyHome();
38 bool KeyEnd();
39 bool KeyActivate();
40
41protected:
42 int32_t iMultiColItemWidth; // if nonzero, the listbox is multicolumn and the column count depends on how many items fit in
43 int32_t iColCount; // number of columns (usually 1)
44 ScrollWindow *pClientWindow; // client scrolling window
45 Element *pSelectedItem; // selected list item
46 BaseCallbackHandler *pSelectionChangeHandler, *pSelectionDblClickHandler;
47 bool fDrawBackground; // whether darker background is to be drawn
48 bool fDrawBorder; // whether 3D frame around box shall be drawn or nay
49 bool fSelectionDisabled; // if set, no entries can be selected
50
51 virtual void DrawElement(C4FacetEx &cgo) override; // draw listbox
52
53 virtual bool IsFocused(Control *pCtrl) override
54 {
55 // subcontrol also counts as focused if the list has focus and the subcontrol is selected
56 return Control::IsFocused(pCtrl) || (HasFocus() && pSelectedItem == pCtrl);
57 }
58
59 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse movement or buttons
60 virtual bool IsFocusOnClick() override { return true; } // list boxes do get focus on click
61 virtual Control *IsFocusElement() override { return this; } // this control can gain focus
62 virtual void OnGetFocus(bool fByMouse) override; // callback when control gains focus - select a list item if none are selected
63 virtual bool CharIn(const char *c) override; // character input for direct list element selection
64
65 virtual void AfterElementRemoval() override
66 {
67 Container::AfterElementRemoval(); UpdateElementPositions();
68 } // called by ScrollWindow to parent after an element has been removed
69
70 void UpdateColumnCount();
71
72public:
73 ListBox(const C4Rect &rtBounds, int32_t iMultiColItemWidth = 0);
74 virtual ~ListBox();
75
76 virtual void RemoveElement(Element *pChild) override; // remove child component
77 bool AddElement(Element *pChild, int32_t iIndent = 0); // add element and adjust its pos
78 bool InsertElement(Element *pChild, Element *pInsertBefore, int32_t iIndent = 0); // insert element and adjust its pos
79 virtual void ElementSizeChanged(Element *pOfElement) override; // called when an element size is changed
80 virtual void ElementPosChanged(Element *pOfElement) override; // called when an element position is changed
81
82 int32_t GetItemWidth() { return iMultiColItemWidth ? iMultiColItemWidth : pClientWindow->GetClientRect().Wdt; }
83
84 void SelectionChanged(bool fByUser); // pSelectedItem changed: sound, tooltip, etc.
85
86 void SetSelectionChangeCallbackFn(BaseCallbackHandler *pToHandler) // update selection change handler
87 {
88 if (pSelectionChangeHandler) pSelectionChangeHandler->DeRef();
89 if ((pSelectionChangeHandler = pToHandler)) pToHandler->Ref();
90 }
91
92 void SetSelectionDblClickFn(BaseCallbackHandler *pToHandler) // update selection doubleclick handler
93 {
94 if (pSelectionDblClickHandler) pSelectionDblClickHandler->DeRef();
95 if ((pSelectionDblClickHandler = pToHandler)) pSelectionDblClickHandler->Ref();
96 }
97
98 void ScrollToBottom() // set scrolling to bottom range
99 {
100 if (pClientWindow) pClientWindow->ScrollToBottom();
101 }
102
103 void ScrollItemInView(Element *pItem); // set scrolling so a specific item is visible
104 void FreezeScrolling() { pClientWindow->Freeze(); }
105 void UnFreezeScrolling() { pClientWindow->UnFreeze(); }
106
107 // change style
108 void SetDecoration(bool fDrawBG, ScrollBarFacets *pToGfx, bool fAutoScroll, bool fDrawBorder = false)
109 {
110 fDrawBackground = fDrawBG; this->fDrawBorder = fDrawBorder; if (pClientWindow) pClientWindow->SetDecoration(pToGfx, fAutoScroll);
111 }
112
113 void SetSelectionDiabled(bool fToVal = true) { fSelectionDisabled = fToVal; }
114
115 // get head and tail list items
116 Element *GetFirst() { return pClientWindow ? pClientWindow->GetFirst() : nullptr; }
117 Element *GetLast() { return pClientWindow ? pClientWindow->GetLast() : nullptr; }
118
119 // get margins from bounds to client rect
120 virtual int32_t GetMarginTop() override { return 3; }
121 virtual int32_t GetMarginLeft() override { return 3; }
122 virtual int32_t GetMarginRight() override { return 3; }
123 virtual int32_t GetMarginBottom() override { return 3; }
124
125 Element *GetSelectedItem() { return pSelectedItem; } // get focused listbox item
126 bool IsScrollingActive() { return pClientWindow && pClientWindow->IsScrollingActive(); }
127 bool IsScrollingNecessary() { return pClientWindow && pClientWindow->IsScrollingNecessary(); }
128 void SelectEntry(Element *pNewSel, bool fByUser);
129 void SelectFirstEntry(bool fByUser) { SelectEntry(pNewSel: GetFirst(), fByUser); }
130 void SelectNone(bool fByUser) { SelectEntry(pNewSel: nullptr, fByUser); }
131 bool IsMultiColumn() const { return iColCount > 1; }
132 int32_t ContractToElementHeight(); // make smaller if elements don't use up all of the available height. Return amount by which list box got contracted
133
134 void UpdateElementPositions(); // reposition list items so they are stacked vertically
135 void UpdateElementPosition(Element *pOfElement, int32_t iIndent); // update pos for one specific element
136 virtual void UpdateSize() override;
137
138 virtual bool IsSelectedChild(Element *pChild) override { return pChild == pSelectedItem || (pSelectedItem && pSelectedItem->IsParentOf(pEl: pChild)); }
139
140 typedef int32_t(*SortFunction)(const Element *pEl1, const Element *pEl2, void *par);
141 void SortElements(SortFunction SortFunc, void *par); // sort list items
142};
143}
144