#include "stdafx.h" #include "TextBox.h" namespace Forms { TextBox::TextBox() : TextBoxBase(), _AcceptsReturn(false), _PasswordChar('\0'), _UseSystemPasswordChar(false), _CharacterCasing(CharacterCasing::Normal), _Scrollbars(ScrollBars::None), _TextAlign(HorizontalAlignment::Left), _SelectionSet(false), _SizeRectDirty(true), _IsCalcRects(false), _NCRectTop{}, _NCRectBottom{}, _NCRectLeft{}, _NCRectRight{} { // We are a textbox control this->_RTTI = ControlTypes::TextBox; } bool TextBox::AcceptsReturn() { return this->_AcceptsReturn; } void TextBox::AcceptsReturn(bool Value) { this->_AcceptsReturn = Value; } CharacterCasing TextBox::GetCharacterCasing() { return this->_CharacterCasing; } void TextBox::SetCharacterCasing(CharacterCasing Value) { if (_CharacterCasing != Value) { _CharacterCasing = Value; UpdateStyles(); } } bool TextBox::PasswordProtect() { return (_PasswordChar != '\0'); } char TextBox::PasswordChar() { if (GetState(ControlStates::StateCreated)) return (char)SendMessageA(this->_Handle, EM_GETPASSWORDCHAR, NULL, NULL); return this->_PasswordChar; } void TextBox::SetPasswordChar(char Value) { _PasswordChar = Value; if (!_UseSystemPasswordChar && GetState(ControlStates::StateCreated) && PasswordChar() != Value) { // Set the password mode SendMessageA(this->_Handle, EM_SETPASSWORDCHAR, Value, NULL); Invalidate(); } } ScrollBars TextBox::GetScrollBars() { return this->_Scrollbars; } void TextBox::SetScrollBars(ScrollBars Value) { if (_Scrollbars != Value) { _Scrollbars = Value; UpdateStyles(); } } void TextBox::SetText(const String& Value) { TextBoxBase::SetText(Value); _SelectionSet = false; } HorizontalAlignment TextBox::TextAlign() { return this->_TextAlign; } void TextBox::SetTextAlign(HorizontalAlignment Value) { if (_TextAlign != Value) { _TextAlign = Value; UpdateStyles(); } } bool TextBox::UseSystemPasswordChar() { return this->_UseSystemPasswordChar; } void TextBox::SetUseSystemPasswordChar(bool Value) { if (_UseSystemPasswordChar != Value) { _UseSystemPasswordChar = Value; UpdateStyles(); } } void TextBox::OnSizeChanged() { if (!GetFlag(TextBoxFlags::FlagMultiline) && !_IsCalcRects) { _SizeRectDirty = true; CalculateSizeRects(); } // We must call the base event last TextBoxBase::OnSizeChanged(); } void TextBox::OnHandleCreated() { TextBoxBase::OnHandleCreated(); if (_PasswordChar != '\0' && !_UseSystemPasswordChar) SendMessageA(this->_Handle, EM_SETPASSWORDCHAR, _PasswordChar, NULL); } void TextBox::OnGotFocus() { TextBoxBase::OnGotFocus(); if (!_SelectionSet) { // We get one shot at selecting when we first get focus. If we don't // do it, we still want to act like the selection was set. _SelectionSet = true; // If the user didn't provide a selection, force one in. if (SelectionLength() == 0 && Control::GetMouseButtons() == MouseButtons::None) SelectAll(); } } void TextBox::WndProc(Message& Msg) { switch (Msg.Msg) { case WM_NCPAINT: WmNcPaint(Msg); break; case WM_NCCALCSIZE: if (!GetFlag(TextBoxFlags::FlagMultiline)) WmNcCalcSize(Msg); else TextBoxBase::WndProc(Msg); break; default: TextBoxBase::WndProc(Msg); break; } } CreateParams TextBox::GetCreateParams() { auto Cp = TextBoxBase::GetCreateParams(); switch (_CharacterCasing) { case CharacterCasing::Lower: Cp.Style |= ES_LOWERCASE; break; case CharacterCasing::Upper: Cp.Style |= ES_UPPERCASE; break; } Cp.ExStyle &= ~WS_EX_RIGHT; switch (_TextAlign) { case HorizontalAlignment::Left: Cp.Style |= ES_LEFT; break; case HorizontalAlignment::Center: Cp.Style |= ES_CENTER; break; case HorizontalAlignment::Right: Cp.Style |= ES_RIGHT; break; } if (Multiline()) { if (((int)_Scrollbars & (int)ScrollBars::Horizontal) == (int)ScrollBars::Horizontal && _TextAlign == HorizontalAlignment::Left && !WordWrap()) Cp.Style |= WS_HSCROLL; if (((int)_Scrollbars & (int)ScrollBars::Vertical) == (int)ScrollBars::Vertical) Cp.Style |= WS_VSCROLL; } if (_UseSystemPasswordChar) Cp.Style |= ES_PASSWORD; return Cp; } void TextBox::WmNcCalcSize(Message& Msg) { if (Msg.WParam == 0) { TextBoxBase::WndProc(Msg); return; } RECT RectWnd, RectClient, RectText{}; GetClientRect(this->_Handle, &RectClient); GetWindowRect(this->_Handle, &RectWnd); auto Font = this->GetFont(); auto DC = GetDC(this->_Handle); auto pOld = SelectObject(DC, Font->GetFontHandle()); DrawTextA(DC, "Ky", 2, &RectText, DT_CALCRECT | DT_LEFT); SelectObject(DC, pOld); ReleaseDC(this->_Handle, DC); MapWindowPoints(this->_Handle, NULL, (LPPOINT)&RectClient, 2); UINT uiVClientHeight = RectText.bottom - RectText.top; auto lpNCSP = (NCCALCSIZE_PARAMS FAR*)Msg.LParam; UINT uiCenterOffset = ((RectClient.bottom - RectClient.top) - uiVClientHeight) / 2; UINT uiCY = ((RectWnd.bottom - RectWnd.top) - (RectClient.bottom - RectClient.top)) / 2; UINT uiCX = ((RectWnd.right - RectWnd.left) - (RectClient.right - RectClient.left)) / 2; // Handle special case where RectClient == RectWnd if (this->GetBorderStyle() == BorderStyle::None) { uiCY = 3; uiCX = 3; uiCenterOffset -= 1; } // A shift to adjust for the off by 1 pixel uiCenterOffset += 1; OffsetRect(&RectWnd, -RectWnd.left, -RectWnd.top); // Handle special case where RectClient == RectWnd if (this->GetBorderStyle() == BorderStyle::None) { _NCRectTop.top = 1; _NCRectTop.left = 1; _NCRectTop.right = RectWnd.right - 1; _NCRectTop.bottom = uiCenterOffset; _NCRectBottom.left = 1; _NCRectBottom.top = uiCenterOffset + uiVClientHeight; _NCRectBottom.right = RectWnd.right - 1; _NCRectBottom.bottom = RectWnd.bottom - 1; _NCRectLeft = _NCRectTop; _NCRectLeft.right = _NCRectLeft.left + 2; _NCRectLeft.bottom = _NCRectBottom.bottom; _NCRectRight = _NCRectTop; _NCRectRight.left = (_NCRectRight.right - 2); _NCRectRight.bottom = _NCRectBottom.bottom; } else { _NCRectTop = RectWnd; _NCRectTop.left += uiCX; _NCRectTop.top += uiCY; _NCRectTop.right -= uiCX; _NCRectTop.bottom -= (uiCenterOffset + uiVClientHeight + uiCY); _NCRectBottom = RectWnd; _NCRectBottom.left += uiCX; _NCRectBottom.top += (uiCenterOffset + uiVClientHeight + uiCY); _NCRectBottom.right -= uiCX; _NCRectBottom.bottom -= uiCY; _NCRectLeft = _NCRectTop; _NCRectLeft.left -= 1; _NCRectLeft.right = _NCRectLeft.left + 1; _NCRectLeft.bottom = _NCRectBottom.bottom; _NCRectRight = _NCRectTop; _NCRectRight.left = (_NCRectRight.right - 1); _NCRectRight.right += 1; _NCRectRight.bottom = _NCRectBottom.bottom; } // Set the resulting client size lpNCSP->rgrc[0].top += uiCenterOffset; lpNCSP->rgrc[0].bottom -= uiCenterOffset; lpNCSP->rgrc[0].left += uiCX; lpNCSP->rgrc[0].right -= uiCY; // They aren't dirty after this routine _SizeRectDirty = false; } void TextBox::WmNcPaint(Message& Msg) { if (!GetFlag(TextBoxFlags::FlagMultiline)) { // Check if we need to recalculate the size rects if (_SizeRectDirty) CalculateSizeRects(); // Paint the missing rects auto DC = GetWindowDC(this->_Handle); auto Brush = (HBRUSH)this->BackColorBrush(); FillRect(DC, &_NCRectLeft, Brush); FillRect(DC, &_NCRectRight, Brush); FillRect(DC, &_NCRectTop, Brush); FillRect(DC, &_NCRectBottom, Brush); ReleaseDC(this->_Handle, DC); } // Ensure default action is taken, WmNcPaint won't touch outside the NC rect... TextBoxBase::WndProc(Msg); } void TextBox::CalculateSizeRects() { _IsCalcRects = true; SetWindowPos(this->_Handle, NULL, 0, 0, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED); _IsCalcRects = false; } }