2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIEditControl.h"
12 #include "GUIKeyboardFactory.h"
13 #include "GUIUserMessages.h"
14 #include "GUIWindowManager.h"
15 #include "LocalizeStrings.h"
16 #include "ServiceBroker.h"
17 #include "XBDateTime.h"
18 #include "dialogs/GUIDialogNumeric.h"
19 #include "input/actions/Action.h"
20 #include "input/actions/ActionIDs.h"
21 #include "input/keyboard/KeyIDs.h"
22 #include "input/keyboard/XBMC_vkeys.h"
23 #include "utils/CharsetConverter.h"
24 #include "utils/ColorUtils.h"
25 #include "utils/Digest.h"
26 #include "utils/Variant.h"
27 #include "utils/log.h"
28 #include "windowing/WinSystem.h"
32 using namespace KODI::GUILIB
;
34 using KODI::UTILITY::CDigest
;
42 constexpr std::string_view smsLetters
[] = {" !@#$%^&*()[]{}<>/\\|0",
53 constexpr float smsDelay
= 1000;
55 // Additional space between left label text and left label text in pixels
56 constexpr float TEXT_SPACE
= 20.0f
;
57 } // unnamed namespace
59 CGUIEditControl::CGUIEditControl(int parentID
, int controlID
, float posX
, float posY
,
60 float width
, float height
, const CTextureInfo
&textureFocus
, const CTextureInfo
&textureNoFocus
,
61 const CLabelInfo
& labelInfo
, const std::string
&text
)
62 : CGUIButtonControl(parentID
, controlID
, posX
, posY
, width
, height
, textureFocus
, textureNoFocus
, labelInfo
)
67 // if skinner forgot to set height
68 if (m_height
== 0 && m_label
.GetLabelInfo().font
)
70 m_height
= m_label
.GetLabelInfo().font
->GetTextHeight(1);
71 CLog::LogF(LOGWARNING
,
72 "No height has been set for GUI edit control ID {}, fallback to font height",
77 void CGUIEditControl::DefaultConstructor()
79 ControlType
= GUICONTROL_EDIT
;
83 m_inputHeading
= g_localizeStrings
.Get(16028);
84 m_inputType
= INPUT_TYPE_TEXT
;
87 m_label2
.GetLabelInfo().offsetX
= 0;
89 m_invalidInput
= false;
90 m_inputValidator
= NULL
;
91 m_inputValidatorData
= NULL
;
96 CGUIEditControl::CGUIEditControl(const CGUIButtonControl
& button
) : CGUIButtonControl(button
)
101 CGUIEditControl::CGUIEditControl(const CGUIEditControl
& button
) : CGUIButtonControl(button
)
103 DefaultConstructor();
106 CGUIEditControl::~CGUIEditControl(void) = default;
108 bool CGUIEditControl::OnMessage(CGUIMessage
&message
)
110 if (message
.GetMessage() == GUI_MSG_SET_TYPE
)
112 SetInputType((INPUT_TYPE
)message
.GetParam1(), message
.GetParam2());
115 else if (message
.GetMessage() == GUI_MSG_ITEM_SELECTED
)
117 message
.SetLabel(GetLabel2());
120 else if (message
.GetMessage() == GUI_MSG_SET_TEXT
&&
121 ((message
.GetControlId() <= 0 && HasFocus()) || (message
.GetControlId() == GetID())))
123 SetLabel2(message
.GetLabel());
126 return CGUIButtonControl::OnMessage(message
);
129 bool CGUIEditControl::OnAction(const CAction
&action
)
133 if (m_inputType
!= INPUT_TYPE_READONLY
)
135 if (action
.GetID() == ACTION_BACKSPACE
)
141 m_text2
.erase(--m_cursorPos
, 1);
146 else if (action
.GetID() == ACTION_MOVE_LEFT
||
147 action
.GetID() == ACTION_CURSOR_LEFT
)
156 else if (action
.GetID() == ACTION_MOVE_RIGHT
||
157 action
.GetID() == ACTION_CURSOR_RIGHT
)
159 if (m_cursorPos
< m_text2
.size())
166 else if (action
.GetID() == ACTION_PASTE
)
172 else if (action
.GetID() >= KEY_VKEY
&& action
.GetID() < KEY_UNICODE
&& m_edit
.empty())
174 // input from the keyboard (vkey, not ascii)
175 unsigned char b
= action
.GetID() & 0xFF;
176 if (b
== XBMCVK_HOME
)
182 else if (b
== XBMCVK_END
)
184 m_cursorPos
= m_text2
.length();
188 if (b
== XBMCVK_LEFT
&& m_cursorPos
> 0)
194 if (b
== XBMCVK_RIGHT
&& m_cursorPos
< m_text2
.length())
200 if (b
== XBMCVK_DELETE
)
202 if (m_cursorPos
< m_text2
.length())
205 m_text2
.erase(m_cursorPos
, 1);
210 if (b
== XBMCVK_BACK
)
215 m_text2
.erase(--m_cursorPos
, 1);
220 else if (b
== XBMCVK_RETURN
|| b
== XBMCVK_NUMPADENTER
)
222 // enter - send click message, but otherwise ignore
223 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 1);
226 else if (b
== XBMCVK_ESCAPE
)
227 { // escape - fallthrough to default action
228 return CGUIButtonControl::OnAction(action
);
231 else if (action
.GetID() == ACTION_KEYBOARD_COMPOSING_KEY
)
233 ComposingCursorAppendChar(action
.GetUnicode());
235 else if (action
.GetID() == ACTION_KEYBOARD_COMPOSING_KEY_CANCELLED
)
237 CancelKeyComposition(action
.GetUnicode());
239 else if (action
.GetID() == ACTION_KEYBOARD_COMPOSING_KEY_FINISHED
)
243 else if (action
.GetID() == KEY_UNICODE
)
245 // input from the keyboard
246 int ch
= action
.GetUnicode();
247 // ignore non-printing characters
248 if ( !((0 <= ch
&& ch
< 0x8) || (0xE <= ch
&& ch
< 0x1B) || (0x1C <= ch
&& ch
< 0x20)) )
252 case 9: // tab, ignore
253 case 11: // Non-printing character, ignore
254 case 12: // Non-printing character, ignore
259 // enter - send click message, but otherwise ignore
260 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 1);
264 { // escape - fallthrough to default action
265 return CGUIButtonControl::OnAction(action
);
273 m_text2
.erase(--m_cursorPos
, 1);
279 if (m_cursorPos
< m_text2
.length())
282 m_text2
.erase(m_cursorPos
, 1);
290 m_text2
.insert(m_text2
.begin() + m_cursorPos
++, action
.GetUnicode());
298 else if (action
.GetID() >= REMOTE_0
&& action
.GetID() <= REMOTE_9
)
299 { // input from the remote
302 OnSMSCharacter(action
.GetID() - REMOTE_0
);
305 else if (action
.GetID() == ACTION_INPUT_TEXT
)
309 g_charsetConverter
.utf8ToW(action
.GetText(), str
, false);
310 m_text2
.insert(m_cursorPos
, str
);
311 m_cursorPos
+= str
.size();
316 return CGUIButtonControl::OnAction(action
);
319 void CGUIEditControl::OnClick()
321 // we received a click - it's not from the keyboard, so pop up the virtual keyboard, unless
322 // that is where we reside!
323 if (GetParentID() == WINDOW_DIALOG_KEYBOARD
)
327 g_charsetConverter
.wToUTF8(m_text2
, utf8
);
328 bool textChanged
= false;
331 case INPUT_TYPE_READONLY
:
334 case INPUT_TYPE_NUMBER
:
335 textChanged
= CGUIDialogNumeric::ShowAndGetNumber(utf8
, m_inputHeading
);
337 case INPUT_TYPE_SECONDS
:
338 textChanged
= CGUIDialogNumeric::ShowAndGetSeconds(utf8
, g_localizeStrings
.Get(21420));
340 case INPUT_TYPE_TIME
:
343 dateTime
.SetFromDBTime(utf8
);
344 KODI::TIME::SystemTime time
;
345 dateTime
.GetAsSystemTime(time
);
346 if (CGUIDialogNumeric::ShowAndGetTime(time
, !m_inputHeading
.empty() ? m_inputHeading
: g_localizeStrings
.Get(21420)))
348 dateTime
= CDateTime(time
);
349 utf8
= dateTime
.GetAsLocalizedTime("", false);
354 case INPUT_TYPE_DATE
:
357 dateTime
.SetFromDBDate(utf8
);
358 if (dateTime
< CDateTime(2000,1, 1, 0, 0, 0))
359 dateTime
= CDateTime(2000, 1, 1, 0, 0, 0);
360 KODI::TIME::SystemTime date
;
361 dateTime
.GetAsSystemTime(date
);
362 if (CGUIDialogNumeric::ShowAndGetDate(date
, !m_inputHeading
.empty() ? m_inputHeading
: g_localizeStrings
.Get(21420)))
364 dateTime
= CDateTime(date
);
365 utf8
= dateTime
.GetAsDBDate();
370 case INPUT_TYPE_IPADDRESS
:
371 textChanged
= CGUIDialogNumeric::ShowAndGetIPAddress(utf8
, m_inputHeading
);
373 case INPUT_TYPE_SEARCH
:
374 textChanged
= CGUIKeyboardFactory::ShowAndGetFilter(utf8
, true);
376 case INPUT_TYPE_FILTER
:
377 textChanged
= CGUIKeyboardFactory::ShowAndGetFilter(utf8
, false);
379 case INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW
:
380 textChanged
= CGUIDialogNumeric::ShowAndVerifyNewPassword(utf8
);
382 case INPUT_TYPE_PASSWORD_MD5
:
383 utf8
= ""; //! @todo Ideally we'd send this to the keyboard and tell the keyboard we have this type of input
386 case INPUT_TYPE_TEXT
:
388 textChanged
= CGUIKeyboardFactory::ShowAndGetInput(utf8
, m_inputHeading
, true, m_inputType
== INPUT_TYPE_PASSWORD
|| m_inputType
== INPUT_TYPE_PASSWORD_MD5
);
395 g_charsetConverter
.utf8ToW(utf8
, m_text2
, false);
396 m_cursorPos
= m_text2
.size();
398 m_cursorPos
= m_text2
.size();
402 void CGUIEditControl::UpdateText(bool sendUpdate
)
409 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 0);
411 m_textChangeActions
.ExecuteActions(GetID(), GetParentID());
416 void CGUIEditControl::SetInputType(CGUIEditControl::INPUT_TYPE type
, const CVariant
& heading
)
419 if (heading
.isString())
420 m_inputHeading
= heading
.asString();
421 else if (heading
.isInteger() && heading
.asInteger())
422 m_inputHeading
= g_localizeStrings
.Get(static_cast<uint32_t>(heading
.asInteger()));
423 //! @todo Verify the current input string?
426 void CGUIEditControl::RecalcRightLabelPosition()
428 // ensure that our cursor is within our width
431 const std::wstring text
= GetDisplayedText();
432 const float textWidth
= m_label2
.CalcTextWidth(text
+ L
'|');
433 const float beforeCursorWidth
= m_label2
.CalcTextWidth(text
.substr(0, m_cursorPos
));
434 const float afterCursorWidth
= m_label2
.CalcTextWidth(text
.substr(0, m_cursorPos
) + L
'|');
435 const float leftTextWidth
= std::min(m_label
.GetTextWidth(), m_label
.GetMaxWidth());
436 float maxTextWidth
= m_width
- 2 * m_label
.GetLabelInfo().offsetX
;
438 if (leftTextWidth
> 0)
439 maxTextWidth
-= leftTextWidth
+ TEXT_SPACE
;
441 if (textWidth
> maxTextWidth
)
442 { // we render taking up the full width, so make sure our cursor position is
443 // within the render window
444 if (m_textOffset
+ afterCursorWidth
> maxTextWidth
)
446 // move the position to the left (outside of the viewport)
447 m_textOffset
= maxTextWidth
- afterCursorWidth
;
449 else if (m_textOffset
+ beforeCursorWidth
< 0) // offscreen to the left
451 // otherwise use original position
452 m_textOffset
= -beforeCursorWidth
;
454 else if (m_textOffset
+ textWidth
< maxTextWidth
)
455 { // we have more text than we're allowed, but we aren't filling all the space
456 m_textOffset
= maxTextWidth
- textWidth
;
463 void CGUIEditControl::ProcessText(unsigned int currentTime
)
465 if (m_smsTimer
.IsRunning() && m_smsTimer
.GetElapsedMilliseconds() > smsDelay
)
468 bool changed
= false;
469 changed
|= m_label
.SetText(m_info
.GetLabel(m_parentID
));
471 m_clipRect
.x1
= m_posX
+ m_label
.GetLabelInfo().offsetX
;
472 m_clipRect
.x2
= m_clipRect
.x1
+ m_width
- 2 * m_label
.GetLabelInfo().offsetX
;
473 m_clipRect
.y1
= m_posY
;
474 m_clipRect
.y2
= m_posY
+ m_height
;
476 // Limit left text max width to 50% of space when focused, otherwise 70%
477 const float maxTextWidth
= m_width
* (HasFocus() ? 0.5f
: 0.7f
);
479 const float leftTextWidth
=
480 std::min(m_label
.GetTextWidth(), maxTextWidth
- 2 * m_label
.GetLabelInfo().offsetX
);
482 changed
|= m_label
.SetMaxRect(m_posX
, m_posY
, maxTextWidth
, m_height
);
486 if (!HasFocus() && leftTextWidth
> 0)
489 RecalcRightLabelPosition();
492 if (leftTextWidth
> 0)
494 // render the text on the left
495 changed
|= m_label
.SetScrolling(HasFocus());
496 changed
|= m_label
.SetColor(GetTextColor());
497 changed
|= m_label
.Process(currentTime
);
499 m_clipRect
.x1
+= leftTextWidth
+ TEXT_SPACE
;
502 // render the text on the right
504 if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_clipRect
.x1
, m_clipRect
.y1
, m_clipRect
.Width(), m_clipRect
.Height()))
506 // set alignment for right label text
507 uint32_t align
= m_label
.GetLabelInfo().align
& XBFONT_CENTER_Y
; // start aligned left
508 if (leftTextWidth
> 0)
509 { // right align as we have 2 labels
510 align
|= XBFONT_RIGHT
;
513 { // align by whatever the skinner requests
514 align
|= (m_label2
.GetLabelInfo().align
& (XBFONT_RIGHT
| XBFONT_CENTER_X
));
517 changed
|= m_label2
.SetMaxRect(m_clipRect
.x1
+ m_textOffset
, m_posY
, m_clipRect
.Width() - m_textOffset
, m_height
);
519 std::wstring text
= GetDisplayedText();
520 std::string hint
= m_hintInfo
.GetLabel(GetParentID());
522 if (!HasFocus() && text
.empty() && !hint
.empty())
524 changed
|= m_label2
.SetText(hint
);
526 else if ((HasFocus() || GetParentID() == WINDOW_DIALOG_KEYBOARD
) &&
527 m_inputType
!= INPUT_TYPE_READONLY
)
529 changed
|= SetStyledText(text
);
532 changed
|= m_label2
.SetTextW(text
);
534 changed
|= m_label2
.SetAlign(align
);
535 changed
|= m_label2
.SetColor(GetTextColor());
537 if (HasFocus() || leftTextWidth
== 0)
538 changed
|= m_label2
.SetOverflow(CGUILabel::OVER_FLOW_CLIP
);
540 changed
|= m_label2
.SetOverflow(CGUILabel::OVER_FLOW_TRUNCATE_LEFT
);
542 changed
|= m_label2
.Process(currentTime
);
543 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
549 void CGUIEditControl::RenderText()
551 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetRenderOrder() ==
552 RENDER_ORDER_FRONT_TO_BACK
)
556 if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_clipRect
.x1
, m_clipRect
.y1
, m_clipRect
.Width(), m_clipRect
.Height()))
559 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
563 CGUILabel::COLOR
CGUIEditControl::GetTextColor() const
565 CGUILabel::COLOR color
= CGUIButtonControl::GetTextColor();
566 if (color
!= CGUILabel::COLOR_DISABLED
&& HasInvalidInput())
567 return CGUILabel::COLOR_INVALID
;
572 void CGUIEditControl::SetHint(const GUIINFO::CGUIInfoLabel
& hint
)
577 std::wstring
CGUIEditControl::GetDisplayedText() const
579 std::wstring
text(m_text2
);
580 if (m_inputType
== INPUT_TYPE_PASSWORD
|| m_inputType
== INPUT_TYPE_PASSWORD_MD5
|| m_inputType
== INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW
)
583 if (m_smsTimer
.IsRunning())
584 { // using the remove to input, so display the last key input
585 text
.append(m_cursorPos
- 1, L
'*');
586 text
.append(1, m_text2
[m_cursorPos
- 1]);
587 text
.append(m_text2
.size() - m_cursorPos
, L
'*');
590 text
.append(m_text2
.size(), L
'*');
592 else if (!m_edit
.empty())
593 text
.insert(m_editOffset
, m_edit
);
597 bool CGUIEditControl::SetStyledText(const std::wstring
&text
)
600 styled
.reserve(text
.size() + 1);
602 std::vector
<KODI::UTILS::COLOR::Color
> colors
;
603 colors
.push_back(m_label
.GetLabelInfo().textColor
);
604 colors
.push_back(m_label
.GetLabelInfo().disabledColor
);
605 KODI::UTILS::COLOR::Color select
= m_label
.GetLabelInfo().selectedColor
;
608 colors
.push_back(select
);
609 colors
.push_back(0x00FFFFFF);
611 unsigned int startHighlight
= m_cursorPos
;
612 unsigned int endHighlight
= m_cursorPos
+ m_edit
.size();
613 unsigned int startSelection
= m_cursorPos
+ m_editOffset
;
614 unsigned int endSelection
= m_cursorPos
+ m_editOffset
+ m_editLength
;
616 CGUIFont
* font
= m_label2
.GetLabelInfo().font
;
617 uint32_t style
= (font
? font
->GetStyle() : (FONT_STYLE_NORMAL
& FONT_STYLE_MASK
)) << 24;
619 for (unsigned int i
= 0; i
< text
.size(); i
++)
621 uint32_t ch
= text
[i
] | style
;
622 if (m_editLength
> 0 && startSelection
<= i
&& i
< endSelection
)
623 ch
|= (2 << 16); // highlight the letters we're playing with
624 else if (!m_edit
.empty() && (i
< startHighlight
|| i
>= endHighlight
))
625 ch
|= (1 << 16); // dim the bits we're not editing
626 styled
.push_back(ch
);
630 unsigned int posChar
= m_cursorPos
;
631 for (const uint32_t& cursorChar
: m_cursorChars
)
633 uint32_t ch
= cursorChar
| style
;
634 if (m_cursorBlinkEnabled
)
636 if ((++m_cursorBlink
% 64) > 32)
639 styled
.insert(styled
.begin() + posChar
, ch
);
642 return m_label2
.SetStyledText(styled
, colors
);
645 void CGUIEditControl::ValidateCursor()
647 if (m_cursorPos
> m_text2
.size())
648 m_cursorPos
= m_text2
.size();
651 void CGUIEditControl::SetLabel(const std::string
&text
)
653 CGUIButtonControl::SetLabel(text
);
657 void CGUIEditControl::SetLabel2(const std::string
&text
)
660 std::wstring newText
;
661 g_charsetConverter
.utf8ToW(text
, newText
, false);
662 if (newText
!= m_text2
)
664 m_isMD5
= (m_inputType
== INPUT_TYPE_PASSWORD_MD5
|| m_inputType
== INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW
);
666 m_cursorPos
= m_text2
.size();
672 std::string
CGUIEditControl::GetLabel2() const
675 g_charsetConverter
.wToUTF8(m_text2
, text
);
676 if (m_inputType
== INPUT_TYPE_PASSWORD_MD5
&& !m_isMD5
)
677 return CDigest::Calculate(CDigest::Type::MD5
, text
);
681 bool CGUIEditControl::ClearMD5()
683 if (!(m_inputType
== INPUT_TYPE_PASSWORD_MD5
|| m_inputType
== INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW
) || !m_isMD5
)
688 if (m_inputType
!= INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW
)
693 unsigned int CGUIEditControl::GetCursorPosition() const
698 void CGUIEditControl::SetCursorPosition(unsigned int iPosition
)
700 m_cursorPos
= iPosition
;
703 void CGUIEditControl::OnSMSCharacter(unsigned int key
)
706 if (m_smsTimer
.IsRunning())
708 // we're already entering an SMS character
709 if (key
!= m_smsLastKey
|| m_smsTimer
.GetElapsedMilliseconds() > smsDelay
)
710 { // a different key was clicked than last time, or we have timed out
715 { // same key as last time within the appropriate time period
718 m_text2
.erase(--m_cursorPos
, 1);
722 { // key is pressed for the first time
727 m_smsKeyIndex
= m_smsKeyIndex
% smsLetters
[key
].size();
729 m_text2
.insert(m_text2
.begin() + m_cursorPos
++, smsLetters
[key
][m_smsKeyIndex
]);
731 m_smsTimer
.StartZero();
734 void CGUIEditControl::OnPasteClipboard()
736 std::wstring unicode_text
;
737 std::string utf8_text
;
739 // Get text from the clipboard
740 utf8_text
= CServiceBroker::GetWinSystem()->GetClipboardText();
741 g_charsetConverter
.utf8ToW(utf8_text
, unicode_text
, false);
743 // Insert the pasted text at the current cursor position.
744 if (unicode_text
.length() > 0)
746 std::wstring left_end
= m_text2
.substr(0, m_cursorPos
);
747 std::wstring right_end
= m_text2
.substr(m_cursorPos
);
750 m_text2
.append(unicode_text
);
751 m_text2
.append(right_end
);
752 m_cursorPos
+= unicode_text
.length();
757 void CGUIEditControl::SetInputValidation(StringValidation::Validator inputValidator
, void *data
/* = NULL */)
759 if (m_inputValidator
== inputValidator
)
762 m_inputValidator
= inputValidator
;
763 m_inputValidatorData
= data
;
764 // the input validator has changed, so re-validate the current data
768 bool CGUIEditControl::ValidateInput(const std::wstring
&data
) const
770 if (m_inputValidator
== NULL
)
773 return m_inputValidator(GetLabel2(), m_inputValidatorData
!= NULL
? m_inputValidatorData
: const_cast<void*>((const void*)this));
776 void CGUIEditControl::ValidateInput()
778 // validate the input
779 bool invalid
= !ValidateInput(m_text2
);
780 // nothing to do if still valid/invalid
781 if (invalid
!= m_invalidInput
)
783 // the validity state has changed so we need to update the control
784 m_invalidInput
= invalid
;
786 // let the window/dialog know that the validity has changed
787 CGUIMessage
msg(GUI_MSG_VALIDITY_CHANGED
, GetID(), GetID(), m_invalidInput
? 0 : 1);
788 SendWindowMessage(msg
);
794 void CGUIEditControl::SetFocus(bool focus
)
797 CGUIControl::SetFocus(focus
);
801 std::string
CGUIEditControl::GetDescriptionByIndex(int index
) const
804 return GetDescription();
811 void CGUIEditControl::ComposingCursorAppendChar(std::uint32_t deadUnicodeKey
)
814 if (m_inputType
== INPUT_TYPE_PASSWORD
|| m_inputType
== INPUT_TYPE_PASSWORD_MD5
||
815 m_inputType
== INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW
)
824 if (IsComposingKey())
826 m_cursorChars
.emplace_back(ch
);
827 m_cursorCharsBuffer
.emplace_back(deadUnicodeKey
);
831 m_cursorChars
= {ch
};
832 m_cursorCharsBuffer
.emplace_back(deadUnicodeKey
);
834 m_cursorBlinkEnabled
= false;
837 void CGUIEditControl::CancelKeyComposition(std::uint32_t deadUnicodeKey
)
839 // sequence cancelled and reverted...
840 if (deadUnicodeKey
== XBMCK_BACKSPACE
)
844 // sequence cancelled and replay...
849 for (const uint32_t& cursorChar
: m_cursorCharsBuffer
)
851 m_text2
.insert(m_text2
.begin() + m_cursorPos
++, cursorChar
);
858 void CGUIEditControl::ResetCursor()
860 m_cursorChars
= {'|'};
861 m_cursorCharsBuffer
.clear();
862 m_cursorBlinkEnabled
= true;
865 bool CGUIEditControl::IsComposingKey() const
867 return !m_cursorBlinkEnabled
;