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 | #include "GUI/BsGUIInputSelection.h" |
4 | #include "Image/BsSpriteTexture.h" |
5 | #include "2D/BsImageSprite.h" |
6 | #include "GUI/BsGUIElement.h" |
7 | #include "GUI/BsGUIManager.h" |
8 | |
9 | namespace bs |
10 | { |
11 | GUIInputSelection::~GUIInputSelection() |
12 | { |
13 | for(auto& sprite : mSprites) |
14 | bs_delete(sprite); |
15 | } |
16 | |
17 | void GUIInputSelection::updateSprite() |
18 | { |
19 | mSelectionRects = getSelectionRects(); |
20 | |
21 | INT32 diff = (INT32)(mSprites.size() - mSelectionRects.size()); |
22 | |
23 | if(diff > 0) |
24 | { |
25 | for(UINT32 i = (UINT32)mSelectionRects.size(); i < (UINT32)mSprites.size(); i++) |
26 | bs_delete(mSprites[i]); |
27 | |
28 | mSprites.erase(mSprites.begin() + mSelectionRects.size(), mSprites.end()); |
29 | } |
30 | else if(diff < 0) |
31 | { |
32 | for(INT32 i = diff; i < 0; i++) |
33 | { |
34 | ImageSprite* newSprite = bs_new<ImageSprite>(); |
35 | mSprites.push_back(newSprite); |
36 | } |
37 | } |
38 | |
39 | const GUIWidget* widget = nullptr; |
40 | if (mElement != nullptr) |
41 | widget = mElement->_getParentWidget(); |
42 | |
43 | UINT32 idx = 0; |
44 | for(auto& sprite : mSprites) |
45 | { |
46 | IMAGE_SPRITE_DESC desc; |
47 | desc.width = mSelectionRects[idx].width; |
48 | desc.height = mSelectionRects[idx].height; |
49 | desc.texture = GUIManager::instance().getTextSelectionTexture(); |
50 | |
51 | sprite->update(desc, (UINT64)widget); |
52 | idx++; |
53 | } |
54 | } |
55 | |
56 | Vector2I GUIInputSelection::getSelectionSpriteOffset(UINT32 spriteIdx) const |
57 | { |
58 | return Vector2I(mSelectionRects[spriteIdx].x, mSelectionRects[spriteIdx].y) + getTextOffset(); |
59 | } |
60 | |
61 | Rect2I GUIInputSelection::getSelectionSpriteClipRect(UINT32 spriteIdx, const Rect2I& parentClipRect) const |
62 | { |
63 | Vector2I selectionOffset(mSelectionRects[spriteIdx].x, mSelectionRects[spriteIdx].y); |
64 | Vector2I clipOffset = selectionOffset + mElement->_getTextInputOffset(); |
65 | |
66 | Rect2I clipRect(-clipOffset.x, -clipOffset.y, mTextDesc.width, mTextDesc.height); |
67 | |
68 | Rect2I localParentCliprect = parentClipRect; |
69 | |
70 | // Move parent rect to our space |
71 | localParentCliprect.x += mElement->_getTextInputOffset().x + clipRect.x; |
72 | localParentCliprect.y += mElement->_getTextInputOffset().y + clipRect.y; |
73 | |
74 | // Clip our rectangle so its not larger then the parent |
75 | clipRect.clip(localParentCliprect); |
76 | |
77 | // Increase clip size by 1, so we can fit the caret in case it is fully at the end of the text |
78 | clipRect.width += 1; |
79 | |
80 | return clipRect; |
81 | } |
82 | |
83 | Vector<Rect2I> GUIInputSelection::getSelectionRects() const |
84 | { |
85 | Vector<Rect2I> selectionRects; |
86 | |
87 | if(mSelectionStart == mSelectionEnd) |
88 | return selectionRects; |
89 | |
90 | UINT32 startLine = getLineForChar(mSelectionStart); |
91 | |
92 | UINT32 endLine = startLine; |
93 | if(mSelectionEnd > 0) |
94 | endLine = getLineForChar(mSelectionEnd - 1, true); |
95 | |
96 | { |
97 | const GUIInputLineDesc& lineDesc = getLineDesc(startLine); |
98 | |
99 | UINT32 startCharIdx = mSelectionStart; |
100 | |
101 | UINT32 endCharIdx = mSelectionEnd - 1; |
102 | if(startLine != endLine) |
103 | { |
104 | endCharIdx = lineDesc.getEndChar(false); |
105 | if(endCharIdx > 0) |
106 | endCharIdx = endCharIdx - 1; |
107 | } |
108 | |
109 | if(!isNewlineChar(startCharIdx) && !isNewlineChar(endCharIdx)) |
110 | { |
111 | Rect2I startChar = getLocalCharRect(startCharIdx); |
112 | Rect2I endChar = getLocalCharRect(endCharIdx); |
113 | |
114 | Rect2I selectionRect; |
115 | selectionRect.x = startChar.x; |
116 | selectionRect.y = lineDesc.getLineYStart(); |
117 | selectionRect.height = lineDesc.getLineHeight(); |
118 | selectionRect.width = (endChar.x + endChar.width) - startChar.x; |
119 | |
120 | selectionRects.push_back(selectionRect); |
121 | } |
122 | } |
123 | |
124 | for(UINT32 i = startLine + 1; i < endLine; i++) |
125 | { |
126 | const GUIInputLineDesc& lineDesc = getLineDesc(i); |
127 | if(lineDesc.getStartChar() == lineDesc.getEndChar() || isNewlineChar(lineDesc.getStartChar())) |
128 | continue; |
129 | |
130 | UINT32 endCharIdx = lineDesc.getEndChar(false); |
131 | if(endCharIdx > 0) |
132 | endCharIdx = endCharIdx - 1; |
133 | |
134 | Rect2I startChar = getLocalCharRect(lineDesc.getStartChar()); |
135 | Rect2I endChar = getLocalCharRect(endCharIdx); |
136 | |
137 | Rect2I selectionRect; |
138 | selectionRect.x = startChar.x; |
139 | selectionRect.y = lineDesc.getLineYStart(); |
140 | selectionRect.height = lineDesc.getLineHeight(); |
141 | selectionRect.width = (endChar.x + endChar.width) - startChar.x; |
142 | |
143 | selectionRects.push_back(selectionRect); |
144 | } |
145 | |
146 | if(startLine != endLine) |
147 | { |
148 | const GUIInputLineDesc& lineDesc = getLineDesc(endLine); |
149 | |
150 | if(lineDesc.getStartChar() != lineDesc.getEndChar() && !isNewlineChar(lineDesc.getStartChar())) |
151 | { |
152 | UINT32 endCharIdx = mSelectionEnd - 1; |
153 | |
154 | if(!isNewlineChar(endCharIdx)) |
155 | { |
156 | Rect2I startChar = getLocalCharRect(lineDesc.getStartChar()); |
157 | Rect2I endChar = getLocalCharRect(endCharIdx); |
158 | |
159 | Rect2I selectionRect; |
160 | selectionRect.x = startChar.x; |
161 | selectionRect.y = lineDesc.getLineYStart(); |
162 | selectionRect.height = lineDesc.getLineHeight(); |
163 | selectionRect.width = (endChar.x + endChar.width) - startChar.x; |
164 | |
165 | selectionRects.push_back(selectionRect); |
166 | } |
167 | } |
168 | } |
169 | |
170 | return selectionRects; |
171 | } |
172 | |
173 | void GUIInputSelection::showSelection(UINT32 anchorCaretPos) |
174 | { |
175 | UINT32 charIdx = getCharIdxAtInputIdx(anchorCaretPos); |
176 | |
177 | mSelectionStart = charIdx; |
178 | mSelectionEnd = charIdx; |
179 | mSelectionAnchor = charIdx; |
180 | } |
181 | |
182 | void GUIInputSelection::clearSelectionVisuals() |
183 | { |
184 | for(auto& sprite : mSprites) |
185 | bs_delete(sprite); |
186 | |
187 | mSprites.clear(); |
188 | } |
189 | |
190 | void GUIInputSelection::selectionDragStart(UINT32 caretPos) |
191 | { |
192 | clearSelectionVisuals(); |
193 | |
194 | showSelection(caretPos); |
195 | mSelectionDragAnchor = caretPos; |
196 | } |
197 | |
198 | void GUIInputSelection::selectionDragUpdate(UINT32 caretPos) |
199 | { |
200 | if(caretPos < mSelectionDragAnchor) |
201 | { |
202 | mSelectionStart = getCharIdxAtInputIdx(caretPos); |
203 | mSelectionEnd = getCharIdxAtInputIdx(mSelectionDragAnchor); |
204 | |
205 | mSelectionAnchor = mSelectionStart; |
206 | } |
207 | |
208 | if(caretPos > mSelectionDragAnchor) |
209 | { |
210 | mSelectionStart = getCharIdxAtInputIdx(mSelectionDragAnchor); |
211 | mSelectionEnd = getCharIdxAtInputIdx(caretPos); |
212 | |
213 | mSelectionAnchor = mSelectionEnd; |
214 | } |
215 | |
216 | if(caretPos == mSelectionDragAnchor) |
217 | { |
218 | mSelectionStart = mSelectionAnchor; |
219 | mSelectionEnd = mSelectionAnchor; |
220 | } |
221 | } |
222 | |
223 | void GUIInputSelection::selectionDragEnd() |
224 | { |
225 | if(isSelectionEmpty()) |
226 | clearSelectionVisuals(); |
227 | } |
228 | |
229 | void GUIInputSelection::moveSelectionToCaret(UINT32 caretPos) |
230 | { |
231 | UINT32 charIdx = getCharIdxAtInputIdx(caretPos); |
232 | |
233 | if(charIdx > mSelectionAnchor) |
234 | { |
235 | mSelectionStart = mSelectionAnchor; |
236 | mSelectionEnd = charIdx; |
237 | } |
238 | else |
239 | { |
240 | mSelectionStart = charIdx; |
241 | mSelectionEnd = mSelectionAnchor; |
242 | } |
243 | |
244 | if(mSelectionStart == mSelectionEnd) |
245 | clearSelectionVisuals(); |
246 | } |
247 | |
248 | void GUIInputSelection::selectAll() |
249 | { |
250 | mSelectionStart = 0; |
251 | mSelectionEnd = mNumChars; |
252 | } |
253 | |
254 | bool GUIInputSelection::isSelectionEmpty() const |
255 | { |
256 | return mSelectionStart == mSelectionEnd; |
257 | } |
258 | } |