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
11namespace 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> _getContextMenu() 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}