1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #pragma once |
4 | |
5 | #include "BsPrerequisites.h" |
6 | #include "GUI/BsGUIElement.h" |
7 | #include "2D/BsImageSprite.h" |
8 | #include "2D/BsTextSprite.h" |
9 | #include "Input/BsVirtualInput.h" |
10 | |
11 | namespace bs |
12 | { |
13 | /** @addtogroup GUI |
14 | * @{ |
15 | */ |
16 | |
17 | /** |
18 | * Input box is a GUI element that accepts Unicode textual input. It can be single or multi-line and handles various |
19 | * types of text manipulation. |
20 | */ |
21 | class BS_EXPORT GUIInputBox : public GUIElement |
22 | { |
23 | /** Possible visual states the input box can be in. */ |
24 | enum class State |
25 | { |
26 | Normal, |
27 | Hover, |
28 | Focused |
29 | }; |
30 | |
31 | public: |
32 | /** Returns type name of the GUI element used for finding GUI element styles. */ |
33 | static const String& getGUITypeName(); |
34 | |
35 | /** |
36 | * Creates a new input box. |
37 | * |
38 | * @param[in] multiline If true the input box can be of arbitrary height and will accept multiple lines of |
39 | * text. |
40 | * @param[in] styleName Optional style to use for the element. Style will be retrieved from GUISkin of the |
41 | * GUIWidget the element is used on. If not specified default style for this element |
42 | * is used. |
43 | */ |
44 | static GUIInputBox* create(bool multiline = false, const String& styleName = StringUtil::BLANK); |
45 | |
46 | /** |
47 | * Creates a new input box. |
48 | * |
49 | * @param[in] multiline If true the input box can be of arbitrary height and will accept multiple lines of |
50 | * text. |
51 | * @param[in] options Options that allow you to control how is the element positioned and sized. This will |
52 | * override any similar options set by style. |
53 | * @param[in] styleName Optional style to use for the element. Style will be retrieved from GUISkin of the |
54 | * GUIWidget the element is used on. If not specified default button style is used. |
55 | */ |
56 | static GUIInputBox* create(bool multiline, const GUIOptions& options, const String& styleName = StringUtil::BLANK); |
57 | |
58 | |
59 | /** |
60 | * Creates a new single-line input box. |
61 | * |
62 | * @param[in] options Options that allow you to control how is the element positioned and sized. This will |
63 | * override any similar options set by style. |
64 | * @param[in] styleName Optional style to use for the element. Style will be retrieved from GUISkin of the |
65 | * GUIWidget the element is used on. If not specified default button style is used. |
66 | */ |
67 | static GUIInputBox* create(const GUIOptions& options, const String& styleName = StringUtil::BLANK); |
68 | |
69 | /** Returns the text currently entered in the input box. */ |
70 | const String& getText() const { return mText; } |
71 | |
72 | /** Sets the text inside the input box. This will replace any current text. */ |
73 | void setText(const String& text); |
74 | |
75 | /** |
76 | * Sets an optional filter that can control what is allowed to be entered into the input box. Filter should return |
77 | * true if the provided string is valid and false otherwise. Set the filter to null to deactivate filtering. |
78 | */ |
79 | void setFilter(std::function<bool(const String&)> filter) { mFilter = filter; } |
80 | |
81 | /** Triggered whenever input text has changed. */ |
82 | Event<void(const String&)> onValueChanged; |
83 | |
84 | /** Triggered when the user hits the Enter key with the input box in focus. */ |
85 | Event<void()> onConfirm; |
86 | |
87 | public: // ***** INTERNAL ****** |
88 | /** @name Internal |
89 | * @{ |
90 | */ |
91 | |
92 | /** @copydoc GUIElement::_getElementType */ |
93 | ElementType _getElementType() const override { return ElementType::InputBox; } |
94 | |
95 | /** @copydoc GUIElement::_getOptimalSize */ |
96 | Vector2I _getOptimalSize() const override; |
97 | |
98 | /** @} */ |
99 | protected: |
100 | GUIInputBox(const String& styleName, const GUIDimensions& dimensions, bool multiline); |
101 | virtual ~GUIInputBox(); |
102 | |
103 | /** @copydoc GUIElement::_getNumRenderElements() */ |
104 | UINT32 _getNumRenderElements() const override; |
105 | |
106 | /** @copydoc GUIElement::_getMaterial() */ |
107 | const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const override; |
108 | |
109 | /** @copydoc GUIElement::_getMeshInfo() */ |
110 | void _getMeshInfo(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices, GUIMeshType& type) const override; |
111 | |
112 | /** @copydoc GUIElement::_fillBuffer() */ |
113 | void _fillBuffer(UINT8* vertices, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset, |
114 | UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 renderElementIdx) const override; |
115 | |
116 | /** @copydoc GUIElement::updateRenderElementsInternal() */ |
117 | void updateRenderElementsInternal() override; |
118 | |
119 | /** @copydoc GUIElement::updateClippedBounds() */ |
120 | void updateClippedBounds() override; |
121 | |
122 | /** @copydoc GUIElement::_mouseEvent */ |
123 | bool _mouseEvent(const GUIMouseEvent& ev) override; |
124 | |
125 | /** @copydoc GUIElement::_textInputEvent */ |
126 | bool _textInputEvent(const GUITextInputEvent& ev) override; |
127 | |
128 | /** @copydoc GUIElement::_commandEvent */ |
129 | bool _commandEvent(const GUICommandEvent& ev) override; |
130 | |
131 | /** @copydoc GUIElement::_virtualButtonEvent */ |
132 | bool _virtualButtonEvent(const GUIVirtualButtonEvent& ev) override; |
133 | |
134 | /** |
135 | * Returns how much to offset text due to scrolling. |
136 | * |
137 | * @note |
138 | * This is used when text is larger than the input box itself. As the caret moves the text will scroll so that the |
139 | * caret remains visible, and how much scroll is applied is determined by this value. |
140 | */ |
141 | Vector2I _getTextInputOffset() const override; |
142 | |
143 | /** Returns rectangle in which the text can be displayed, in local coordinates (text will start at 0, 0). */ |
144 | Rect2I _getTextInputRect() const override; |
145 | |
146 | /** @copydoc GUIElement::_getRenderElementDepth */ |
147 | UINT32 _getRenderElementDepth(UINT32 renderElementIdx) const override; |
148 | |
149 | /** @copydoc GUIElement::_getRenderElementDepthRange */ |
150 | UINT32 _getRenderElementDepthRange() const override; |
151 | |
152 | /** @copydoc GUIElement::_hasCustomCursor */ |
153 | bool _hasCustomCursor(const Vector2I position, CursorType& type) const override; |
154 | |
155 | /** @copydoc GUIElement::_getContextMenu */ |
156 | SPtr<GUIContextMenu> () const override; |
157 | private: |
158 | /** |
159 | * Retrieves a sprite from a render element index, and a local render element index that represents render element |
160 | * within the returned sprite. |
161 | */ |
162 | Sprite* renderElemToSprite(UINT32 renderElemIdx, UINT32& localRenderElemIdx) const; |
163 | |
164 | /** |
165 | * Returns offset at which is the element with the provided render element index. Offset is relative to parent |
166 | * widget. |
167 | */ |
168 | Vector2I renderElemToOffset(UINT32 renderElemIdx) const; |
169 | |
170 | /** |
171 | * Returns a clip rectangle that can be used for clipping the render element with the provided index. Rectangle is |
172 | * in local coordiantes relative to element origin. |
173 | */ |
174 | Rect2I renderElemToClipRect(UINT32 renderElemIdx) const; |
175 | |
176 | /** Inserts a new string into the current text at the specified index. */ |
177 | void insertString(UINT32 charIdx, const String& string); |
178 | |
179 | /** Inserts a new character into the current text at the specified index. */ |
180 | void insertChar(UINT32 charIdx, UINT32 charCode); |
181 | |
182 | /** Erases a single character at the specified index. */ |
183 | void eraseChar(UINT32 charIdx); |
184 | |
185 | /** |
186 | * Deletes text that is currently selected. |
187 | * |
188 | * @param[in] internal If internal no filter will be applied after the text is deleted, and no event will be |
189 | * triggered either. |
190 | */ |
191 | void deleteSelectedText(bool internal = false); |
192 | |
193 | /** Returns currently selected text. */ |
194 | String getSelectedText(); |
195 | |
196 | /** Shows the input caret. You must position the caret manually after showing it. */ |
197 | void showCaret(); |
198 | |
199 | /** Hides the input caret. */ |
200 | void hideCaret(); |
201 | |
202 | /** |
203 | * Shows selection with the specified anchor position. You must position selection start and end before selection |
204 | * will actually render. Anchor position determines selection behavior as the user moves the selection with the |
205 | * keyboard. |
206 | */ |
207 | void showSelection(UINT32 anchorCaretPos); |
208 | |
209 | /** Removes any active selection. */ |
210 | void clearSelection(); |
211 | |
212 | /** Adjusts the text offset (scroll) so that the caret is visible. */ |
213 | void scrollTextToCaret(); |
214 | |
215 | /** Clamps the text offset (scroll) so that the text fits in the provided bounds nicely with minimal white space. */ |
216 | void clampScrollToBounds(Rect2I unclippedTextBounds); |
217 | |
218 | /** Returns offset at which to render the text. Relative to parent widget. */ |
219 | Vector2I getTextOffset() const; |
220 | |
221 | /** Returns rectangle used for clipping the text. Relative to element. */ |
222 | Rect2I getTextClipRect() const; |
223 | |
224 | /** Returns text sprite descriptor determining how is text sprite created. */ |
225 | TEXT_SPRITE_DESC getTextDesc() const; |
226 | |
227 | /** Returns currently active input box texture, depending on active state. */ |
228 | const HSpriteTexture& getActiveTexture() const; |
229 | |
230 | /** Returns currently active input box text color, depending on active state. */ |
231 | Color getActiveTextColor() const; |
232 | |
233 | /** Cuts currently selected text to clipboard. */ |
234 | void cutText(); |
235 | |
236 | /** Copies currently selected text to clipboard. */ |
237 | void copyText(); |
238 | |
239 | /** Inserts text from clipboard to current caret location. */ |
240 | void pasteText(); |
241 | |
242 | private: |
243 | static VirtualButton mCopyVB; |
244 | static VirtualButton mPasteVB; |
245 | static VirtualButton mCutVB; |
246 | static VirtualButton mSelectAllVB; |
247 | |
248 | // Sprites |
249 | ImageSprite* mImageSprite; |
250 | TextSprite* mTextSprite; |
251 | bool mIsMultiline; |
252 | Vector2I mTextOffset; |
253 | bool mHasFocus = false; |
254 | UINT64 mFocusGainedFrame = (UINT64)-1; |
255 | bool mIsMouseOver = false; |
256 | State mState = State::Normal; |
257 | |
258 | IMAGE_SPRITE_DESC mImageDesc; |
259 | String mText; |
260 | UINT32 mNumChars = 0; |
261 | std::function<bool(const String&)> mFilter; |
262 | |
263 | bool mCaretShown = false; |
264 | bool mSelectionShown = false; |
265 | bool mDragInProgress = false; |
266 | }; |
267 | |
268 | /** @} */ |
269 | } |