r5sdk/r5dev/thirdparty/cppnet/cppkore/TextBoxBase.cpp
Kawe Mazidjatari 04bee896be Fix string/wstring type conflict
cppkore uses string/wstring as StringBase while we use std::string/std::wstring as string/wstring. Changed all types in cppkore to String/WString instead.
2022-05-21 21:51:35 +02:00

659 lines
14 KiB
C++

#include "stdafx.h"
#include "TextBoxBase.h"
namespace Forms
{
TextBoxBase::TextBoxBase()
: Control(), _Flags((TextBoxFlags)0), _BorderStyle(BorderStyle::Fixed3D), _MaxLength(32767), _SelectionStart(0), _SelectionLength(0), _DoubleClickFired(false)
{
SetFlag(TextBoxFlags::FlagAutoSize | TextBoxFlags::FlagHideSelection | TextBoxFlags::FlagWordWrap | TextBoxFlags::FlagShortcutsEnabled, true);
// TODO: Re-evaluate after setting auto-size flag properly... SetStyle(ControlStyles::FixedHeight, GetFlag(TextBoxFlags::FlagAutoSize));
SetStyle(ControlStyles::StandardClick |
ControlStyles::StandardDoubleClick |
ControlStyles::UseTextForAccessibility |
ControlStyles::UserPaint, false);
// Default back color for text based controls
_BackColor = { 255, 255, 255 };
}
void TextBoxBase::OnMouseUp(const std::unique_ptr<MouseEventArgs>& EventArgs)
{
auto Pt = this->PointToScreen({ EventArgs->X, EventArgs->Y });
if (EventArgs->Button == MouseButtons::Left)
{
if (WindowFromPoint({ Pt.X, Pt.Y }) == this->_Handle)
{
if (!this->_DoubleClickFired)
{
OnClick();
OnMouseClick(EventArgs);
}
else
{
this->_DoubleClickFired = false;
OnDoubleClick();
OnMouseDoubleClick(EventArgs);
}
}
this->_DoubleClickFired = false;
}
Control::OnMouseUp(EventArgs);
}
void TextBoxBase::OnHideSelectionChanged()
{
HideSelectionChanged.RaiseEvent(this);
}
void TextBoxBase::OnModifiedChanged()
{
ModifiedChanged.RaiseEvent(this);
}
void TextBoxBase::OnMultilineChanged()
{
MultilineChanged.RaiseEvent(this);
}
void TextBoxBase::OnReadOnlyChanged()
{
ReadOnlyChanged.RaiseEvent(this);
}
void TextBoxBase::OnAcceptsTabChanged()
{
AcceptsTabChanged.RaiseEvent(this);
}
void TextBoxBase::OnHandleCreated()
{
Control::OnHandleCreated();
if (GetFlag(TextBoxFlags::FlagSetSelectionOnHandleCreated))
{
Select(this->_SelectionStart, this->_SelectionLength);
SetFlag(TextBoxFlags::FlagScrollToCaretOnHandleCreated, false);
}
this->UpdateMaxLength();
if (GetFlag(TextBoxFlags::FlagModified))
SendMessageA(this->_Handle, EM_SETMODIFY, 1, NULL);
if (GetFlag(TextBoxFlags::FlagScrollToCaretOnHandleCreated))
{
ScrollToCaret();
SetFlag(TextBoxFlags::FlagScrollToCaretOnHandleCreated, false);
}
}
void TextBoxBase::OnHandleDestroyed()
{
SetFlag(TextBoxFlags::FlagModified, this->Modified());
SetFlag(TextBoxFlags::FlagSetSelectionOnHandleCreated, true);
GetSelectionStartAndLength(this->_SelectionStart, this->_SelectionLength);
// Call the base event last
Control::OnHandleDestroyed();
}
BorderStyle TextBoxBase::GetBorderStyle()
{
return this->_BorderStyle;
}
void TextBoxBase::SetBorderStyle(BorderStyle Value)
{
this->_BorderStyle = Value;
Invalidate();
UpdateStyles();
}
bool TextBoxBase::AcceptsTab()
{
return GetFlag(TextBoxFlags::FlagAcceptsTab);
}
void TextBoxBase::SetAcceptsTab(bool Value)
{
if (GetFlag(TextBoxFlags::FlagAcceptsTab) != Value)
{
SetFlag(TextBoxFlags::FlagAcceptsTab, Value);
OnAcceptsTabChanged();
}
}
bool TextBoxBase::CanUndo()
{
return (SendMessageA(this->_Handle, EM_CANUNDO, NULL, NULL) == 0);
}
bool TextBoxBase::HideSelection()
{
return GetFlag(TextBoxFlags::FlagHideSelection);
}
void TextBoxBase::SetHideSelection(bool Value)
{
if (GetFlag(TextBoxFlags::FlagHideSelection) != Value)
{
SetFlag(TextBoxFlags::FlagHideSelection, Value);
Invalidate();
UpdateStyles();
OnHideSelectionChanged();
}
}
List<String> TextBoxBase::Lines()
{
List<String> Result;
auto Buffer = this->Text();
uint32_t LineStart = 0;
while (LineStart < Buffer.Length())
{
uint32_t LineEnd = LineStart;
for (; LineEnd < Buffer.Length(); LineEnd++)
{
char c = Buffer[LineEnd];
if (c == '\r' || c == '\n')
break;
}
Result.EmplaceBack(Buffer.SubString(LineStart, LineEnd - LineStart));
// Treat "\r", "\r\n", and "\n" as new lines
if (LineEnd < Buffer.Length() && Buffer[LineEnd] == '\r')
LineEnd++;
if (LineEnd < Buffer.Length() && Buffer[LineEnd] == '\n')
LineEnd++;
LineStart = LineEnd;
}
if (Buffer.Length() > 0 && (Buffer[Buffer.Length() - 1] == '\r' || Buffer[Buffer.Length() - 1] == '\n'))
Result.EmplaceBack("");
return Result;
}
void TextBoxBase::SetLines(const List<String>& Value)
{
String Result = "";
for (auto& Line : Value)
{
Result.Append(Line);
Result.Append((char*)"\r\n");
}
this->SetText(Result);
}
uint32_t TextBoxBase::MaxLength()
{
return this->_MaxLength;
}
void TextBoxBase::SetMaxLength(uint32_t Value)
{
if (this->_MaxLength != Value)
{
this->_MaxLength = Value;
UpdateMaxLength();
}
}
String TextBoxBase::Text()
{
return Control::Text();
}
void TextBoxBase::SetText(const String& Value)
{
if (Value != Control::Text())
{
Control::SetText(Value);
if (GetState(ControlStates::StateCreated))
SendMessageA(this->_Handle, EM_SETMODIFY, 0, NULL);
}
}
uint32_t TextBoxBase::TextLength()
{
if (GetState(ControlStates::StateCreated))
return (uint32_t)GetWindowTextLengthA(this->_Handle);
return Text().Length();
}
bool TextBoxBase::WordWrap()
{
return GetFlag(TextBoxFlags::FlagWordWrap);
}
void TextBoxBase::SetWordWrap(bool Value)
{
if (WordWrap() != Value)
{
SetFlag(TextBoxFlags::FlagWordWrap, Value);
UpdateStyles();
}
}
void TextBoxBase::AppendText(const String& Text)
{
int32_t SelStart, SelLength;
GetSelectionStartAndLength(SelStart, SelLength);
int32_t EndOfText = this->GetEndPosition();
SelectInternal(EndOfText, EndOfText, EndOfText);
SetSelectedText(Text);
if (this->_Width == 0 || this->_Height == 0)
Select(SelStart, SelLength);
}
void TextBoxBase::Clear()
{
this->SetText("");
}
void TextBoxBase::ClearUndo()
{
if (GetState(ControlStates::StateCreated))
SendMessageA(this->_Handle, EM_EMPTYUNDOBUFFER, 0, 0);
}
void TextBoxBase::Copy()
{
SendMessageA(this->_Handle, WM_COPY, NULL, NULL);
}
void TextBoxBase::Cut()
{
SendMessageA(this->_Handle, WM_CUT, NULL, NULL);
}
void TextBoxBase::Paste()
{
SendMessageA(this->_Handle, WM_PASTE, NULL, NULL);
}
void TextBoxBase::Undo()
{
SendMessageA(this->_Handle, EM_UNDO, NULL, NULL);
}
void TextBoxBase::Select(int32_t Start, int32_t Length)
{
int TextLen = this->TextLength();
if (Start > TextLen)
{
// Convert to negative length if we're at the end...
int64_t LongLength = min(0, (int64_t)Length + Start - TextLen);
if (LongLength < INT_MIN)
Length = INT_MIN;
else
Length = (int32_t)LongLength;
Start = TextLen;
}
SelectInternal(Start, Length, TextLen);
}
void TextBoxBase::SelectAll()
{
auto Len = this->TextLength();
SelectInternal(0, Len, Len);
}
void TextBoxBase::DeselectAll()
{
this->SetSelectionLength(0);
}
void TextBoxBase::ScrollToCaret()
{
if (GetState(ControlStates::StateCreated))
SendMessageA(this->_Handle, EM_SCROLLCARET, NULL, NULL);
else
SetFlag(TextBoxFlags::FlagScrollToCaretOnHandleCreated, true);
}
CreateParams TextBoxBase::GetCreateParams()
{
auto Cp = Control::GetCreateParams();
Cp.ClassName = "EDIT";
Cp.Style |= ES_AUTOHSCROLL | ES_AUTOVSCROLL;
if (!GetFlag(TextBoxFlags::FlagHideSelection))
Cp.Style |= ES_NOHIDESEL;
if (GetFlag(TextBoxFlags::FlagReadOnly))
Cp.Style |= ES_READONLY;
Cp.ExStyle &= (~WS_EX_CLIENTEDGE);
Cp.Style &= (~WS_BORDER);
switch (this->_BorderStyle)
{
case BorderStyle::Fixed3D:
Cp.ExStyle |= WS_EX_CLIENTEDGE;
break;
case BorderStyle::FixedSingle:
Cp.Style |= WS_BORDER;
break;
}
if (GetFlag(TextBoxFlags::FlagMultiline))
{
Cp.Style |= ES_MULTILINE;
if (GetFlag(TextBoxFlags::FlagWordWrap))
Cp.Style &= ~ES_AUTOHSCROLL;
}
return Cp;
}
void TextBoxBase::SetWindowText(const String& Value)
{
// Override to prevent double OnTextChanged events
if (this->WindowText() != Value)
{
SetFlag(TextBoxFlags::FlagCodeUpdateText, true);
Control::SetWindowText(Value);
SetFlag(TextBoxFlags::FlagCodeUpdateText, false);
}
}
bool TextBoxBase::CanRaiseTextChangedEvent()
{
return true;
}
void TextBoxBase::UpdateMaxLength()
{
if (GetState(ControlStates::StateCreated))
SendMessageA(this->_Handle, EM_LIMITTEXT, (WPARAM)this->_MaxLength, NULL);
}
void TextBoxBase::SetSelectedTextInternal(const String& Text, bool ClearUndo)
{
SendMessageA(this->_Handle, EM_LIMITTEXT, 0, 0);
if (ClearUndo)
{
SendMessageA(this->_Handle, EM_REPLACESEL, 0, (LPARAM)(char*)Text);
SendMessageA(this->_Handle, EM_SETMODIFY, 0, 0);
this->ClearUndo();
}
else
SendMessageA(this->_Handle, EM_REPLACESEL, -1, (LPARAM)(char*)Text);
SendMessageA(this->_Handle, EM_LIMITTEXT, this->_MaxLength, NULL);
}
void TextBoxBase::SelectInternal(int32_t Start, int32_t Length, int32_t TextLen)
{
if (GetState(ControlStates::StateCreated))
{
int32_t St, Ed;
AdjustSelectionStartAndEnd(Start, Length, St, Ed, TextLen);
SendMessageA(this->_Handle, EM_SETSEL, (WPARAM)St, (LPARAM)Ed);
}
else
{
this->_SelectionStart = Start;
this->_SelectionLength = Length;
SetFlag(TextBoxFlags::FlagSetSelectionOnHandleCreated, true);
}
}
int32_t TextBoxBase::GetEndPosition()
{
return GetState(ControlStates::StateCreated) ? TextLength() + 1 : TextLength();
}
void TextBoxBase::GetSelectionStartAndLength(int32_t& Start, int32_t& Length)
{
int32_t End = 0;
if (!GetState(ControlStates::StateCreated))
{
AdjustSelectionStartAndEnd(this->_SelectionStart, this->_SelectionLength, Start, End, -1);
Length = End - Start;
}
else
{
Start = 0;
SendMessageA(this->_Handle, EM_GETSEL, (WPARAM)&Start, (LPARAM)&End);
Start = (int32_t)max(0, Start);
End = (int32_t)max(0, End);
Length = End - Start;
}
}
void TextBoxBase::AdjustSelectionStartAndEnd(int32_t SelStart, int32_t SelLength, int32_t& Start, int32_t& End, int32_t TextLen)
{
Start = SelStart;
End = 0;
if (Start <= -1)
Start = -1;
else
{
int32_t TextLength;
if (TextLen >= 0)
TextLength = TextLen;
else
TextLength = this->TextLength();
if (Start > TextLength)
Start = TextLength;
// Easy check for overflow when we add them
if ((int64_t)((int64_t)Start + (int64_t)SelLength) > INT_MAX)
End = Start > 0 ? INT_MAX : INT_MIN;
else
End = Start + SelLength;
// End must always be positive and less than total
if (End < 0)
End = 0;
else if (End > TextLength)
End = TextLength;
}
}
bool TextBoxBase::Modified()
{
if (GetState(ControlStates::StateCreated))
{
auto CurState = (0 != (int)(long)SendMessageA(this->_Handle, EM_GETMODIFY, NULL, NULL));
if (GetFlag(TextBoxFlags::FlagModified) != CurState)
{
SetFlag(TextBoxFlags::FlagModified, CurState);
OnModifiedChanged();
}
return CurState;
}
return GetFlag(TextBoxFlags::FlagModified);
}
void TextBoxBase::SetModified(bool Value)
{
if (Modified() != Value)
{
if (GetState(ControlStates::StateCreated))
SendMessageA(this->_Handle, EM_SETMODIFY, Value ? 1 : 0, NULL);
SetFlag(TextBoxFlags::FlagModified, Value);
OnModifiedChanged();
}
}
bool TextBoxBase::Multiline()
{
return GetFlag(TextBoxFlags::FlagMultiline);
}
void TextBoxBase::SetMultiline(bool Value)
{
if (GetFlag(TextBoxFlags::FlagMultiline) != Value)
{
SetFlag(TextBoxFlags::FlagMultiline, Value);
UpdateStyles();
OnMultilineChanged();
}
}
bool TextBoxBase::PasswordProtect()
{
return false;
}
bool TextBoxBase::ReadOnly()
{
return GetFlag(TextBoxFlags::FlagReadOnly);
}
void TextBoxBase::SetReadOnly(bool Value)
{
if (GetFlag(TextBoxFlags::FlagReadOnly) != Value)
{
SetFlag(TextBoxFlags::FlagReadOnly, Value);
if (GetState(ControlStates::StateCreated))
SendMessageA(this->_Handle, EM_SETREADONLY, Value ? -1 : 0, NULL);
OnReadOnlyChanged();
}
}
String TextBoxBase::SelectedText()
{
int32_t SelStart, SelLength;
GetSelectionStartAndLength(SelStart, SelLength);
return Text().SubString(SelStart, SelLength);
}
void TextBoxBase::SetSelectedText(const String& Value)
{
SetSelectedTextInternal(Value, true);
}
int32_t TextBoxBase::SelectionLength()
{
int32_t Length, Junk;
GetSelectionStartAndLength(Junk, Length);
return Length;
}
void TextBoxBase::SetSelectionLength(int32_t Value)
{
int32_t SelStart, SelLength;
GetSelectionStartAndLength(SelStart, SelLength);
if (Value != SelLength)
Select(SelStart, Value);
}
int32_t TextBoxBase::SelectionStart()
{
int32_t Start, Junk;
GetSelectionStartAndLength(Start, Junk);
return Start;
}
void TextBoxBase::SetSelectionStart(int32_t Value)
{
Select(Value, this->SelectionLength());
}
bool TextBoxBase::GetFlag(TextBoxFlags Flag)
{
return ((int)this->_Flags & (int)Flag) == (int)Flag;
}
void TextBoxBase::SetFlag(TextBoxFlags Flags, bool Value)
{
this->_Flags = Value ? (TextBoxFlags)((int)this->_Flags | (int)Flags) : (TextBoxFlags)((int)this->_Flags & ~(int)Flags);
}
void TextBoxBase::WmReflectCommand(Message& Msg)
{
if (!GetFlag(TextBoxFlags::FlagCodeUpdateText))
{
if (HIWORD(Msg.WParam) == EN_CHANGE && CanRaiseTextChangedEvent())
OnTextChanged();
else if (HIWORD(Msg.WParam) == EN_UPDATE)
Modified();
}
}
void TextBoxBase::WmGetDlgCode(Message& Msg)
{
Control::WndProc(Msg);
if (this->AcceptsTab())
Msg.Result = (uintptr_t)((int)Msg.Result | DLGC_WANTTAB);
else
Msg.Result = (uintptr_t)((int)Msg.Result & ~(DLGC_WANTTAB | DLGC_WANTALLKEYS));
}
void TextBoxBase::WmSetFont(Message& Msg)
{
Control::WndProc(Msg);
if (!GetFlag(TextBoxFlags::FlagMultiline))
SendMessageA(this->_Handle, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, 0);
}
void TextBoxBase::WndProc(Message& Msg)
{
switch (Msg.Msg)
{
case WM_LBUTTONDBLCLK:
_DoubleClickFired = true;
Control::WndProc(Msg);
break;
case WM_REFLECT:
WmReflectCommand(Msg);
break;
case WM_GETDLGCODE:
WmGetDlgCode(Msg);
break;
case WM_SETFONT:
WmSetFont(Msg);
break;
default:
Control::WndProc(Msg);
break;
}
}
}