| 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 | |
| 22 | namespace C4GUI |
| 23 | { |
| 24 | // a vertical list of elements |
| 25 | class ListBox : public Control |
| 26 | { |
| 27 | private: |
| 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 | |
| 41 | protected: |
| 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 | |
| 72 | public: |
| 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 | |