#include "TextBox.h" #undef min #undef max namespace ezui { TextBox::TextBox(Object* parentObject) :Control(parentObject) { Init(); } TextBox::~TextBox() { m_timer->Stop(); if (m_textLayout) { delete m_textLayout; } if (m_font) { delete m_font; } } void TextBox::Init() { this->GetScrollBar()->SetWidth(5); this->GetScrollBar()->Parent = this; this->GetScrollBar()->OffsetCallback = [=](int offset) { this->Offset(offset); }; Style.Cursor = LoadCursor(Cursor::IBEAM); m_timer = new Timer(this); m_timer->Interval = 500; m_timer->Tick = [this](Timer* t) { HWND hWnd = this->Hwnd();//在对象析构前获取句柄 BeginInvoke([this, hWnd]() {//捕获hWnd if (!::IsWindow(hWnd))return;//如果窗口已经销毁 则不往下执行 if (!this->IsEnabled() || this->IsReadOnly()) { m_bCareShow = false; return; } if (!m_careRect.IsEmptyArea() && m_focus) { m_bCareShow = !m_bCareShow; this->Invalidate(); } }); }; } void TextBox::OnRemove() { __super::OnRemove(); m_timer->Stop(); } void TextBox::SetPadding(int px, int py) { // 兼容旧接口:水平=px 垂直=py SetPadding4(px, py, px, py); } void TextBox::SetPadding4(int left, int top, int right, int bottom) { left = std::max(0, left); top = std::max(0, top); right = std::max(0, right); bottom = std::max(0, bottom); if (m_padLeft != left || m_padTop != top || m_padRight != right || m_padBottom != bottom) { m_padLeft = left; m_padTop = top; m_padRight = right; m_padBottom = bottom; Analysis(); Invalidate(); } } bool TextBox::ApplyStyleProperty(const UIString& key, const UIString& value) { // 先让基类尝试 if (__super::ApplyStyleProperty(key, value)) return true; auto parseInt = [](const UIString& v)->int { UIString tmp = v; ui_text::Replace(&tmp, "px", ""); tmp = tmp.trim(); return std::atoi(tmp.c_str()); }; // 新增:四边独立 & 兼容旧属性 if (key == "padding-left") { SetPadding4(parseInt(value), m_padTop, m_padRight, m_padBottom); return true; } if (key == "padding-right") { SetPadding4(m_padLeft, m_padTop, parseInt(value), m_padBottom); return true; } if (key == "padding-top") { SetPadding4(m_padLeft, parseInt(value), m_padRight, m_padBottom); return true; } if (key == "padding-bottom") { SetPadding4(m_padLeft, m_padTop, m_padRight, parseInt(value)); return true; } if (key == "padding-x") { int p = parseInt(value); SetPadding4(p, m_padTop, p, m_padBottom); return true; } if (key == "padding-y") { int p = parseInt(value); SetPadding4(m_padLeft, p, m_padRight, p); return true; } if (key == "padding") { UIString v = value; ui_text::Replace(&v, " ", ","); auto arr = v.split(","); if (arr.size() == 1) { int p = parseInt(arr[0]); SetPadding4(p, p, p, p); return true; } if (arr.size() == 2) { // 兼容旧语义:水平 垂直 int px = parseInt(arr[0]); int py = parseInt(arr[1]); SetPadding4(px, py, px, py); return true; } if (arr.size() == 3) { // CSS: top horizontal bottom int pTop = parseInt(arr[0]); int pH = parseInt(arr[1]); int pBottom = parseInt(arr[2]); SetPadding4(pH, pTop, pH, pBottom); return true; } if (arr.size() == 4) { // top right bottom left int pTop = parseInt(arr[0]); int pRight = parseInt(arr[1]); int pBottom = parseInt(arr[2]); int pLeft = parseInt(arr[3]); SetPadding4(pLeft, pTop, pRight, pBottom); return true; } return true; // 不合法也拦截 } return false; } void TextBox::SetAutoWidth(bool flag) { //需要屏蔽 } void TextBox::SetAutoHeight(bool flag) { //需要屏蔽 } void TextBox::OnKeyChar(const KeyboardEventArgs& arg) { __super::OnKeyChar(arg); WPARAM wParam = arg.wParam; LPARAM lParam = arg.lParam; if (wParam < 32)return;//控制字符 if (IsReadOnly()) return;//只读 WCHAR buf[2]{ (WCHAR)wParam ,0 };// InsertUnicode(std::wstring(buf));//插入新的字符 Analysis();//分析字符串 Invalidate();//刷新 } #undef max #undef min void TextBox::BuildSelectedRect() { m_selectRects.clear(); if (m_textLayout) { //获取起点和终点 Point point1, point2; if ((m_A_TextPos + m_A_isTrailingHit) < (m_B_TextPos + m_B_isTrailingHit)) { point1 = m_textLayout->HitTestTextPosition(m_A_TextPos, m_A_isTrailingHit); point2 = m_textLayout->HitTestTextPosition(m_B_TextPos, m_B_isTrailingHit); } else { point2 = m_textLayout->HitTestTextPosition(m_A_TextPos, m_A_isTrailingHit); point1 = m_textLayout->HitTestTextPosition(m_B_TextPos, m_B_isTrailingHit); } // 为了稳定行判定,对基线 Y 加一个行高一半偏移放到行中部 float sampleY1 = point1.Y + m_textLayout->GetFontHeight() / 2.0f; float sampleY2 = point2.Y + m_textLayout->GetFontHeight() / 2.0f; auto lineRects = m_textLayout->GetLineRects(); for (auto& lr : lineRects) { bool firstLine = (sampleY1 >= lr.Y && sampleY1 < lr.Y + lr.Height + 0.5f); bool lastLine = (sampleY2 >= lr.Y && sampleY2 < lr.Y + lr.Height + 0.5f); if (firstLine && lastLine) { // 同一行 float leftX = point1.X; float rightX = point2.X; if (rightX < leftX) std::swap(leftX, rightX); float width = std::max(0.0f, rightX - leftX); m_selectRects.push_back(Rect(leftX, lr.Y, width, lr.Height)); } else if (firstLine) { // 首行:到行尾 float width = std::max(0.0f, (lr.X + lr.Width) - point1.X); m_selectRects.push_back(Rect(point1.X, lr.Y, width, lr.Height)); } else if (lastLine) { // 末行:从行首 float width = std::max(0.0f, point2.X - lr.X); m_selectRects.push_back(Rect(lr.X, lr.Y, width, lr.Height)); } else if (sampleY1 < lr.Y && sampleY2 >= lr.Y + lr.Height) { // 中间整行 m_selectRects.push_back(lr); } } } } bool TextBox::SelectedAll() { if (m_textLayout && !m_text.empty()) { m_pointA = Point{ 0,0 }; m_A_isTrailingHit = FALSE; m_A_TextPos = 0; m_pointB = Point{ m_fontBox.Width ,0 }; m_B_isTrailingHit = TRUE; m_B_TextPos = m_text.size() - 1; BuildSelectedRect(); return true; } return false; } bool TextBox::GetSelectedRange(int* outPos, int* outCount) { if (m_selectRects.size() > 0) { int pos, count; if ((m_A_TextPos + m_A_isTrailingHit) < (m_B_TextPos + m_B_isTrailingHit)) { int pos1 = m_A_TextPos; if (m_A_isTrailingHit == 1) { pos1 += 1; } int pos2 = m_B_TextPos; if (m_B_isTrailingHit == 0) { pos2 -= 1; } pos = pos1; count = std::abs(pos2 - pos1) + 1; } else { int pos1 = m_A_TextPos; if (m_A_isTrailingHit == 0) { pos1 -= 1; } int pos2 = m_B_TextPos; if (m_B_isTrailingHit == 1) { pos2 += 1; } pos = pos2; count = std::abs(pos2 - pos1) + 1; } *outPos = pos; *outCount = count; if (*outCount > 0) { return true; } } return false; } void TextBox::InsertUnicode(const std::wstring& str, bool isEnd) { DeleteRange();//先删除是否有选中的区域 if (m_textPos < 0) m_textPos = 0; // 处理光标位置 if (m_textPos > (int)m_text.size()) { m_textPos = m_text.size(); } if (isEnd) m_textPos = m_text.size(); // 是否让光标移到末尾 m_text.insert(m_textPos, str); m_textPos += str.size(); if (TextChanged) { TextChanged(UIString(m_text)); } } bool TextBox::DeleteRange() { int pos, count; if (GetSelectedRange(&pos, &count)) {//删除选中的 //isTrailingHit = FALSE; m_textPos = pos; m_text.erase(pos, count); if (TextChanged) { TextChanged(UIString(m_text)); } return true; } return false; } bool TextBox::Copy() { int pos, count; if (!GetSelectedRange(&pos, &count))return false; std::wstring wBuf(m_text.substr(pos, count)); return ezui::CopyToClipboard(wBuf, Hwnd()); } bool TextBox::Paste() { std::wstring wBuf; bool bRet = ezui::GetClipboardData(&wBuf, Hwnd()); UIString u8Str(wBuf); if (!m_allowManualLineBreak) { //不允许手动换行时不允许有换行符 ui_text::Replace(&u8Str, "\r", ""); ui_text::Replace(&u8Str, "\n", ""); } InsertUnicode(u8Str.unicode());//插入新的字符 return bRet; } void TextBox::OnBackspace() { if (m_text.size() <= 0)return; if (!DeleteRange()) {//先看看有没有有选中的需要删除 //否则删除单个字符 m_textPos--; if (m_textPos > -1) { m_text.erase(m_textPos, 1); if (TextChanged) { TextChanged(UIString(m_text)); } } } } void TextBox::OnKeyDown(const KeyboardEventArgs& arg) { __super::OnKeyDown(arg); WPARAM wParam = arg.wParam; LPARAM lParam = arg.lParam; //判断是否按下shift+enter(仅当允许手动换行时) if (m_allowManualLineBreak && (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_RETURN) { InsertUnicode(L"\n");//插入换行符 Analysis();//分析字符串 Invalidate();//刷新 return; } //判断是否按下Ctrl键 if (GetKeyState(VK_CONTROL) & 0x8000) { do { if (wParam == 'A') {//ctrl+a全选 SelectedAll();//全选 Invalidate();//刷新 break; } if (wParam == 'C') {//ctrl+c 复制 Copy(); break; } if (wParam == 'X') {//ctrl+x 剪切 if (IsReadOnly()) { break; } if (Copy()) { DeleteRange();//因为是剪切 所以要删除选中的这段 Analysis(); Invalidate();//刷新 } break; } if (wParam == 'V') {//ctrl+v 粘贴 if (IsReadOnly()) { break; } Paste(); Analysis();//分析字符串 Invalidate();//刷新 break; } if (wParam == 'Z') {//ctrl+z撤销 if (IsReadOnly()) { break; } break; } } while (false); return; } if (wParam == VK_BACK) { //退格键(删除字符) if (IsReadOnly()) { return; } OnBackspace();//退格键的操作在里面 Analysis();//重新分析 Invalidate();//刷新 return; } if (wParam == VK_LEFT) { m_textPos--; m_bCareShow = true; m_selectRects.clear(); BuildCare(); Invalidate(); return; } if (wParam == VK_RIGHT) { ++m_textPos; m_bCareShow = true; m_selectRects.clear(); BuildCare(); Invalidate(); return; } } void TextBox::Analysis() { if (m_text.size() > (size_t)(this->m_maxLen)) { m_text.erase((size_t)(this->m_maxLen)); } m_scrollX = 0; m_scrollY = 0; m_pointA = Point(); m_A_isTrailingHit = 0; m_A_TextPos = 0; m_pointB = Point(); m_B_isTrailingHit = 0; m_B_TextPos = 0; m_careRect = Rect(); m_selectRects.clear(); if (m_font == NULL) return; if (m_textLayout) delete m_textLayout; std::wstring* drawText = &this->m_text; if (!m_passwordChar.empty()) { drawText = new std::wstring; int count = m_passwordChar.size() * m_text.size(); for (size_t i = 0; i < m_text.size(); ++i) { *drawText += m_passwordChar; } } else { *drawText = m_text; } // 可用绘制宽高(考虑内边距) int innerW = std::max(0, Width() - (m_padLeft + m_padRight)); int innerH = std::max(0, Height() - (m_padTop + m_padBottom)); if (!m_autoWrap) {//不自动换行时需要水平拖动滚动编辑框(两者都不允许) m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); bool bAlignLeft = (int(this->TextAlign) & int(HAlign::Left)); float width = bAlignLeft ? EZUI_FLOAT_MAX : (float)innerW; m_textLayout = new TextLayout(*drawText, *m_font, SizeF{ width,(float)innerH }, this->TextAlign); m_fontBox = m_textLayout->GetFontBox(); if (m_fontBox.Width < innerW) { m_scrollX = 0; } if (!bAlignLeft && m_fontBox.Width > innerW) { ezui::TextAlign tmp = this->TextAlign; tmp = ezui::TextAlign((int)tmp & ~(int)HAlign::Center); tmp = ezui::TextAlign((int)tmp & ~(int)HAlign::Right); tmp = ezui::TextAlign((int)tmp | (int)HAlign::Left); m_textLayout->SetTextAlign(tmp); } if (m_fontBox.Width > innerW && m_scrollX + m_fontBox.Width < innerW) { m_scrollX = innerW - m_fontBox.Width; } } else {//多行编辑框(至少允许一种换行方式) // 根据 m_autoWrap 决定是否启用自动换行 if (m_autoWrap) { m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP); } else { m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); } m_textLayout = new TextLayout(*drawText, *m_font, SizeF{ (float)innerW, EZUI_FLOAT_MAX }, TextAlign::TopLeft); m_fontBox = m_textLayout->GetFontBox(); } if (drawText != &this->m_text) { delete drawText; } // 内容尺寸:多行时加入 padding 以保证最后一行不被裁掉,单行保持原逻辑 if (m_autoWrap || m_allowManualLineBreak) { this->SetContentSize({ m_fontBox.Width + m_padLeft + m_padRight, m_fontBox.Height + m_padTop + m_padBottom }); } else { this->SetContentSize({ m_fontBox.Width , m_fontBox.Height }); } if (m_autoWrap || m_allowManualLineBreak) { this->GetScrollBar()->RefreshScroll(); } BuildCare(); } void TextBox::BuildCare() { if (!m_textLayout) return; if (m_textPos < 0) { m_textPos = 0; } if (m_textPos > (int)m_text.size()) { m_textPos = m_text.size(); } Point pt = m_textLayout->HitTestTextPosition(m_textPos, FALSE); m_careRect.X = pt.X; m_careRect.Y = pt.Y; m_careRect.Height = m_textLayout->GetFontHeight(); m_careRect.Width = 1 * this->GetScale(); // 处理水平滚动(当不自动换行时需要水平滚动) if (!m_autoWrap) { // 使光标保持在可视内区(考虑 padding) int innerW = std::max(0, Width() - (m_padLeft + m_padRight)); int caretDrawX = m_careRect.X + m_scrollX; // 布局坐标 + 滚动 if (caretDrawX < 0) { // 左越界 m_scrollX -= caretDrawX; } if (caretDrawX > innerW) { // 右越界 int offsetX = innerW - caretDrawX; m_scrollX += offsetX; } } // 处理垂直滚动(多行模式需要垂直滚动) if (m_autoWrap || m_allowManualLineBreak) { int innerH = std::max(0, Height() - (m_padTop + m_padBottom)); int caretDrawY = m_careRect.Y + m_scrollY; // 仅布局+滚动 if (caretDrawY < 0) { m_scrollY -= caretDrawY; } int caretBottom = caretDrawY + m_careRect.Height; if (caretBottom > innerH) { m_scrollY -= (caretBottom - innerH); } } } void TextBox::OnMouseDown(const MouseEventArgs& arg) { __super::OnMouseDown(arg); m_lastX = 0; m_lastY = 0; auto mbtn = arg.Button; auto point = arg.Location; if (mbtn == MouseButton::Left) { m_down = true; m_point_Start = ConvertPoint(point); if (m_textLayout) { int fontHeight; m_selectRects.clear(); m_pointA = m_textLayout->HitTestPoint(m_point_Start, &m_A_TextPos, &m_A_isTrailingHit, &fontHeight); m_careRect.X = m_pointA.X; m_careRect.Y = m_pointA.Y; m_careRect.Width = 1; m_careRect.Height = fontHeight; m_textPos = m_A_TextPos; if (m_A_isTrailingHit) { ++m_textPos; } } Invalidate(); } } void TextBox::OnMouseWheel(const MouseEventArgs& arg) { __super::OnMouseWheel(arg); if (!m_autoWrap) {//不自动换行时需要水平拖动滚动 int innerW = std::max(0, Width() - (m_padLeft + m_padRight)); int textWidth = m_fontBox.Width; if (arg.ZDelta > 0 && textWidth > innerW) { m_scrollX += std::abs(arg.ZDelta) * 0.5; if (m_scrollX > 0) { m_scrollX = 0; } Invalidate(); } else if (arg.ZDelta<0 && textWidth>innerW) { m_scrollX -= std::abs(arg.ZDelta) * 0.5; if (-m_scrollX + innerW > textWidth) { m_scrollX = -(textWidth - innerW); } Invalidate(); } } } ScrollBar* TextBox::GetScrollBar() { return &m_vScrollbar; } void TextBox::Offset(int _sliderY) { this->m_scrollY = _sliderY; Invalidate(); } void TextBox::OnLayout() { __super::OnLayout(); m_scrollX = 0; m_scrollY = 0; m_selectRects.clear(); if ((!m_autoWrap && !m_allowManualLineBreak) && Height() != m_lastHeight) { m_lastHeight = Height(); Analysis(); } if ((m_autoWrap || m_allowManualLineBreak) && Width() != m_lastWidth) { m_lastWidth = Width(); Analysis(); } this->SetContentSize({ m_fontBox.Width ,(m_autoWrap || m_allowManualLineBreak) ? m_fontBox.Height : Height() }); this->GetScrollBar()->RefreshScroll(); this->EndLayout(); } Point TextBox::ConvertPoint(const Point& pt) { // 将控件坐标转换为文本布局坐标:去掉滚动与内边距偏移 int _x = -m_scrollX - m_padLeft; int _y = -m_scrollY - m_padTop; return Point{ pt.X + _x, pt.Y + _y }; } void TextBox::OnMouseMove(const MouseEventArgs& arg) { __super::OnMouseMove(arg); auto point = arg.Location; if (m_down) { m_point_End = ConvertPoint(point); if (m_textLayout) { int fontHeight; m_selectRects.clear();// m_pointB = m_textLayout->HitTestPoint(m_point_End, &m_B_TextPos, &m_B_isTrailingHit, &fontHeight); BuildSelectedRect(); if (!m_autoWrap) {//不自动换行时需要水平拖动滚动 // 计算去掉左 padding 的鼠标相对文本区域坐标 int innerW = std::max(0, Width() - (m_padLeft + m_padRight)); int textWidth = m_fontBox.Width; int localX = point.X - m_padLeft; // 鼠标在文本可编辑区域内的 X if (m_lastX > point.X) { // 向左拖 m_lastX = point.X; if (textWidth > innerW && m_scrollX < 0 && localX < 0) { m_scrollX += 3; Invalidate(); return; } } if (m_lastX < point.X) { // 向右拖 m_lastX = point.X; if (textWidth > innerW && localX > innerW) { m_scrollX -= 3; if (-m_scrollX + innerW > textWidth) { m_scrollX = -(textWidth - innerW); } Invalidate(); return; } } } Invalidate(); } } } void TextBox::OnMouseUp(const MouseEventArgs& arg) { __super::OnMouseUp(arg); m_down = false; m_lastX = 0; m_lastY = 0; Invalidate(); } void TextBox::OnFocus(const FocusEventArgs& arg) { __super::OnFocus(arg); m_focus = true; m_bCareShow = true; m_timer->Start(); Invalidate(); } void TextBox::OnKillFocus(const KillFocusEventArgs& arg) { __super::OnKillFocus(arg); m_down = false; m_focus = false; m_bCareShow = false; m_timer->Stop(); this->Invalidate(); } const UIString TextBox::GetText() { return UIString(this->m_text); } void TextBox::SetText(const UIString& text) { this->m_text = text.unicode(); Analysis(); } bool TextBox::IsMultiLine() { return m_autoWrap || m_allowManualLineBreak; } void TextBox::SetMultiLine(bool autoWrap, bool allowManualLineBreak) { if (this->m_autoWrap != autoWrap || this->m_allowManualLineBreak != allowManualLineBreak) { this->m_autoWrap = autoWrap; this->m_allowManualLineBreak = allowManualLineBreak; Analysis(); } } void TextBox::SetReadOnly(bool bReadOnly) { this->m_readOnly = bReadOnly; } bool TextBox::IsReadOnly() { return this->m_readOnly; } void TextBox::SetMaxLength(int maxLen) { this->m_maxLen = maxLen; } void TextBox::SetPlaceholderText(const UIString& text) { this->m_placeholder = text.unicode(); } void TextBox::SetPasswordChar(const UIString& passwordChar) { this->m_passwordChar = passwordChar.unicode(); } void TextBox::SetFocusAndCaret(int caretPos) { // 设置焦点状态 m_focus = true; m_bCareShow = true; m_timer->Start(); // 设置光标位置 if (caretPos < 0) { m_textPos = (int)m_text.size(); // -1 表示末尾 } else { m_textPos = caretPos; } // 确保 m_textPos 在有效范围内 if (m_textPos < 0) m_textPos = 0; if (m_textPos > (int)m_text.size()) m_textPos = (int)m_text.size(); // 清除选中状态 m_selectRects.clear(); m_pointA = Point(); m_pointB = Point(); m_A_TextPos = m_textPos; m_B_TextPos = m_textPos; // 需要重绘以确保 m_font 和 m_textLayout 被初始化 // 之后 OnForePaint 中会调用 BuildCare Invalidate(); } Rect TextBox::GetCareRect() { Rect rect(m_careRect); rect.X += m_scrollX + m_padLeft; // 滚动 + padding rect.Y += m_scrollY + m_padTop; return rect; } void TextBox::Insert(const UIString& str, bool isEnd) { InsertUnicode(str.unicode(), isEnd); Analysis();//分析字符串 } void TextBox::SetAttribute(const UIString& key, const UIString& value) { __super::SetAttribute(key, value); do { if (key == "padding" || key == "padding-x" || key == "padding-y") { // 走统一样式解析,直接调用 ApplyStyleProperty ApplyStyleProperty(key, value); break; } if (key == "valign") { this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)VAlign::Top); this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)VAlign::Mid); this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)VAlign::Bottom); VAlign v = VAlign::Mid; if (value == "top") { v = VAlign::Top; } else if (value == "bottom") { v = VAlign::Bottom; } this->TextAlign = ezui::TextAlign((int)this->TextAlign | (int)v); break; } if (key == "halign") { this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)HAlign::Left); this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)HAlign::Center); this->TextAlign = ezui::TextAlign((int)this->TextAlign & ~(int)HAlign::Right); HAlign h = HAlign::Center; if (value == "left") { h = HAlign::Left; } else if (value == "right") { h = HAlign::Right; } this->TextAlign = ezui::TextAlign((int)this->TextAlign | (int)h); break; } if (key == "passwordchar") { this->SetPasswordChar(value); break; } if (key == "placeholder") { this->SetPlaceholderText(value); break; } if (key == "text" || key == "value") { this->SetText(value); break; } if (key == "readonly") { if (value == "true") { this->SetReadOnly(true); break; } if (value == "false") { this->SetReadOnly(false); break; } } if (key == "multiline") { if (value == "true") { // 兼容旧版:multiline=true 表示不自动换行+手动换行 this->m_autoWrap = false; this->m_allowManualLineBreak = true; break; } if (value == "false") { this->m_autoWrap = false; this->m_allowManualLineBreak = false; break; } } } while (false); } void TextBox::OnForePaint(PaintEventArgs& e) { std::wstring fontFamily = GetFontFamily(); auto fontSize = GetFontSize(); if (fontSize == 0)return; if (m_font == NULL || ((m_font != NULL) && (m_font->GetFontFamily() != fontFamily || m_font->GetFontSize() != fontSize))) { if (m_font != NULL) { delete m_font; } m_font = new Font(fontFamily, fontSize); Analysis(); } // 如果有焦点但光标未初始化,重新构建光标 if (m_focus && m_careRect.IsEmptyArea() && m_textLayout) { BuildCare(); } Color fontColor = GetForeColor(); e.Graphics.SetFont(fontFamily, fontSize); if (m_text.empty()) { bool bAlignCenter = (int(this->TextAlign) & int(HAlign::Center)); if ((!bAlignCenter) || (bAlignCenter && !m_focus)) {//避免光标和placeholder重叠 Color placeholderColor = fontColor; placeholderColor.SetA(fontColor.GetA() * 0.6); e.Graphics.SetColor(placeholderColor); RectF ph((float)m_padLeft, (float)m_padTop, (float)Width() - (float)(m_padLeft + m_padRight), (float)Height() - (float)(m_padTop + m_padBottom)); Font phFont(fontFamily, fontSize); e.Graphics.SetFont(phFont); e.Graphics.DrawString(m_placeholder, ph, (m_autoWrap || m_allowManualLineBreak) ? TextAlign::TopLeft : this->TextAlign); } } if (m_selectRects.size() > 0) { Color selectedColor = fontColor; selectedColor.SetA(fontColor.GetA() * 0.35); e.Graphics.SetColor(selectedColor); for (auto& it : m_selectRects) { if (!it.IsEmptyArea()) { RectF rect(it); rect.X += m_scrollX + m_padLeft; // 加上内边距 rect.Y += m_scrollY + m_padTop; e.Graphics.FillRectangle(rect); } } } if (m_textLayout) { e.Graphics.SetColor(fontColor); PointF base{ (float)m_scrollX + (float)m_padLeft, (float)m_scrollY + (float)m_padTop }; e.Graphics.DrawTextLayout(*m_textLayout, base); } if (!m_careRect.IsEmptyArea() && m_focus) { if (m_bCareShow) { RectF rect(m_careRect.X + m_padLeft, m_careRect.Y + m_padTop, m_careRect.Width, m_careRect.Height); rect.X += m_scrollX;//偏移 rect.Y += m_scrollY; if (ezui::IsFloatEqual(rect.X, this->Width())) { rect.X = this->Width() - rect.Width; //末尾微调 } rect.Width = rect.Width * this->GetScale(); e.Graphics.SetColor(fontColor); e.Graphics.FillRectangle(rect); } } } }