Files
EzUI/sources/TextBox.cpp

794 lines
22 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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_multiLine) {
//行编辑框不允许有换行符
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 (IsMultiLine() && (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_multiLine) {//单行编辑框
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_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_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_multiLine) {
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_multiLine) {
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_multiLine) {
// 使光标保持在可视内区(考虑 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;
}
}
else { // 多行:处理垂直滚动保持光标可见
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_multiLine) {//单行
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_multiLine && Height() != m_lastHeight) {
m_lastHeight = Height();
Analysis();
}
if (m_multiLine && Width() != m_lastWidth) {
m_lastWidth = Width();
Analysis();
}
this->SetContentSize({ m_fontBox.Width ,m_multiLine ? 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_multiLine) {//单行
// 计算去掉左 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_multiLine;
}
void TextBox::SetMultiLine(bool multiLine)
{
if (this->m_multiLine != multiLine) {
this->m_multiLine = multiLine;
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();
}
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") {
this->m_multiLine = true;
break;
}
if (value == "false") {
this->m_multiLine = 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();
}
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));
e.Graphics.DrawString(m_placeholder, ph, m_multiLine ? 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);
}
}
}
}