@@ -260,6 +260,11 @@ void CUi::Update(vec2 MouseWorldPos)
260260 m_pHotScrollRegion = nullptr ;
261261 }
262262
263+ // Reset selectable label state when it was not rendered for a frame
264+ if (!m_SelectableLabelState.m_Rendered )
265+ m_SelectableLabelState.Reset ();
266+ m_SelectableLabelState.m_Rendered = false ;
267+
263268 m_ProgressSpinnerOffset += Client ()->RenderFrameTime () * 1 .5f ;
264269 m_ProgressSpinnerOffset = std::fmod (m_ProgressSpinnerOffset, 1 .0f );
265270}
@@ -809,6 +814,101 @@ CLabelResult CUi::DoLabel(const CUIRect *pRect, const char *pText, float Size, i
809814 return CLabelResult{.m_Truncated = Cursor.m_Truncated };
810815}
811816
817+ CLabelResult CUi::DoSelectableLabel (const void *pId, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps)
818+ {
819+ const bool Inside = MouseHovered (pRect);
820+ CSelectableLabelState *pState = &m_SelectableLabelState;
821+ const bool IsOwner = pState->m_pActiveId == pId;
822+ pState->m_Rendered = true ;
823+
824+ CLineInput::SMouseSelection *pMouseSelection = &pState->m_MouseSelection ;
825+ if (CheckActiveItem (pId))
826+ {
827+ if (!MouseButton (0 ))
828+ {
829+ SetActiveItem (nullptr );
830+ pMouseSelection->m_Selecting = false ;
831+ }
832+ }
833+ else if (HotItem () == pId)
834+ {
835+ if (MouseButton (0 ))
836+ {
837+ SetActiveItem (pId);
838+ pMouseSelection->m_Selecting = true ;
839+ pMouseSelection->m_PressMouse = MousePos ();
840+ pState->m_pActiveId = pId;
841+ pState->m_SelectionStart = 0 ;
842+ pState->m_SelectionEnd = 0 ;
843+ }
844+ }
845+
846+ if (Inside && !MouseButton (0 ))
847+ SetHotItem (pId);
848+
849+ if (pMouseSelection->m_Selecting )
850+ pMouseSelection->m_ReleaseMouse = MousePos ();
851+
852+ // Click outside clears selection, only for the label that owns it
853+ if (!Inside && MouseButtonClicked (0 ) && !CheckActiveItem (pId) && IsOwner)
854+ pState->Reset ();
855+
856+ // DoLabel logic
857+ const int Flags = GetFlagsForLabelProperties (LabelProps, nullptr );
858+ const SCursorAndBoundingBox TextBounds = CalcFontSizeCursorHeightAndBoundingBox (TextRender (), pText, Flags, Size, pRect->w , LabelProps);
859+ const vec2 CursorPos = CalcAlignedCursorPos (pRect, TextBounds.m_TextSize , Align, TextBounds.m_LineCount == 1 ? &TextBounds.m_BiggestCharacterHeight : nullptr );
860+
861+ CTextCursor Cursor;
862+ Cursor.SetPosition (CursorPos);
863+ Cursor.m_FontSize = Size;
864+ Cursor.m_Flags |= Flags;
865+ Cursor.m_vColorSplits = LabelProps.m_vColorSplits ;
866+ Cursor.m_LineWidth = (float )LabelProps.m_MaxWidth ;
867+
868+ if (pMouseSelection->m_Selecting && IsOwner)
869+ {
870+ Cursor.m_CalculateSelectionMode = TEXT_CURSOR_SELECTION_MODE_CALCULATE ;
871+ Cursor.m_PressMouse = pMouseSelection->m_PressMouse ;
872+ Cursor.m_ReleaseMouse = pMouseSelection->m_ReleaseMouse ;
873+ }
874+ else if (pState->HasSelection () && IsOwner)
875+ {
876+ Cursor.m_CalculateSelectionMode = TEXT_CURSOR_SELECTION_MODE_SET ;
877+ Cursor.m_SelectionStart = pState->m_SelectionStart ;
878+ Cursor.m_SelectionEnd = pState->m_SelectionEnd ;
879+ }
880+
881+ TextRender ()->TextEx (&Cursor, pText, -1 );
882+
883+ if (pMouseSelection->m_Selecting && IsOwner)
884+ {
885+ pState->m_SelectionStart = Cursor.m_SelectionStart ;
886+ pState->m_SelectionEnd = Cursor.m_SelectionEnd ;
887+ }
888+
889+ if ((pState->HasSelection () && IsOwner) || Inside)
890+ {
891+ if (Input ()->ModifierIsPressed () && Input ()->KeyPress (KEY_A ))
892+ {
893+ pState->m_pActiveId = pId;
894+ pState->m_SelectionStart = 0 ;
895+ pState->m_SelectionEnd = Cursor.m_GlyphCount ;
896+ }
897+ }
898+
899+ if (pState->HasSelection () && IsOwner && Input ()->ModifierIsPressed () && Input ()->KeyPress (KEY_C ))
900+ {
901+ const int Start = minimum (pState->m_SelectionStart , pState->m_SelectionEnd );
902+ const int End = maximum (pState->m_SelectionStart , pState->m_SelectionEnd );
903+ const size_t StartBytes = str_utf8_offset_chars_to_bytes (pText, Start);
904+ const size_t EndBytes = str_utf8_offset_chars_to_bytes (pText, End);
905+ std::string SelectedText (pText + StartBytes, EndBytes - StartBytes);
906+ Input ()->SetClipboardText (SelectedText.c_str ());
907+ }
908+
909+ return CLabelResult{.m_Truncated = Cursor.m_Truncated };
910+ }
911+
812912void CUi::DoLabel (CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps, int StrLen, const CTextCursor *pReadCursor) const
813913{
814914 const int Flags = GetFlagsForLabelProperties (LabelProps, pReadCursor);
0 commit comments