Skip to content

Commit 5d047dc

Browse files
committed
Add Selectable Label
1 parent 3f4b9ed commit 5d047dc

3 files changed

Lines changed: 126 additions & 3 deletions

File tree

‎src/game/client/components/menus.cpp‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,12 +1349,13 @@ void CMenus::RenderPopupFullscreen(CUIRect Screen)
13491349
{
13501350
const float ExtraTextFontSize = m_Popup == POPUP_FIRST_LAUNCH ? 16.0f : 20.0f;
13511351

1352+
static CButtonContainer s_ExtraTextLabel;
13521353
if(TopAlign)
1353-
Ui()->DoLabel(&ExtraText, pExtraText, ExtraTextFontSize, TEXTALIGN_TL, {.m_MaxWidth = ExtraText.w});
1354+
Ui()->DoSelectableLabel(&s_ExtraTextLabel, &ExtraText, pExtraText, ExtraTextFontSize, TEXTALIGN_TL, {.m_MaxWidth = ExtraText.w});
13541355
else if(TextRender()->TextWidth(ExtraTextFontSize, pExtraText) > ExtraText.w)
1355-
Ui()->DoLabel(&ExtraText, pExtraText, ExtraTextFontSize, TEXTALIGN_ML, {.m_MaxWidth = ExtraText.w});
1356+
Ui()->DoSelectableLabel(&s_ExtraTextLabel, &ExtraText, pExtraText, ExtraTextFontSize, TEXTALIGN_ML, {.m_MaxWidth = ExtraText.w});
13561357
else
1357-
Ui()->DoLabel(&ExtraText, pExtraText, ExtraTextFontSize, TEXTALIGN_MC);
1358+
Ui()->DoSelectableLabel(&s_ExtraTextLabel, &ExtraText, pExtraText, ExtraTextFontSize, TEXTALIGN_MC);
13581359
}
13591360
}
13601361

‎src/game/client/ui.cpp‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
812912
void 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);

‎src/game/client/ui.h‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,27 @@ class CUi
383383
};
384384
CDoubleClickState m_DoubleClickState;
385385
const void *m_pLastEditingItem = nullptr;
386+
387+
class CSelectableLabelState
388+
{
389+
public:
390+
CLineInput::SMouseSelection m_MouseSelection;
391+
const void *m_pActiveId = nullptr;
392+
bool m_Rendered = false;
393+
int m_SelectionStart = 0;
394+
int m_SelectionEnd = 0;
395+
396+
bool HasSelection() const { return m_SelectionStart != m_SelectionEnd; }
397+
void Reset()
398+
{
399+
m_MouseSelection.m_Selecting = false;
400+
m_pActiveId = nullptr;
401+
m_SelectionStart = 0;
402+
m_SelectionEnd = 0;
403+
}
404+
};
405+
CSelectableLabelState m_SelectableLabelState;
406+
386407
const void *m_pLastActiveScrollbar = nullptr;
387408
int m_ScrollbarValue = 0;
388409
float m_ActiveScrollbarOffset = 0.0f;
@@ -589,6 +610,7 @@ class CUi
589610

590611
CLabelResult DoLabel(const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {}) const;
591612
CLabelResult DoLabel_AutoLineSize(const char *pText, float FontSize, int Align, CUIRect *pRect, float LineSize, const SLabelProperties &LabelProps = {}) const;
613+
CLabelResult DoSelectableLabel(const void *pId, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {});
592614

593615
void DoLabel(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {}, int StrLen = -1, const CTextCursor *pReadCursor = nullptr) const;
594616
void DoLabelStreamed(CUIElement::SUIElementRect &RectEl, const CUIRect *pRect, const char *pText, float Size, int Align, const SLabelProperties &LabelProps = {}, int StrLen = -1, const CTextCursor *pReadCursor = nullptr) const;

0 commit comments

Comments
 (0)