修复了一些BUG,增强了一些功能,详情查看更新日志
This commit is contained in:
+16
-15
@@ -22,9 +22,9 @@
|
|||||||
#include"label.h"
|
#include"label.h"
|
||||||
|
|
||||||
#define DISABLEDCOLOUR RGB(96, 96, 96) //禁用状态颜色
|
#define DISABLEDCOLOUR RGB(96, 96, 96) //禁用状态颜色
|
||||||
#define TEXTMARGINS_X 6
|
#define TEXTMARGINS_X 6
|
||||||
#define TEXTMARGINS_Y 4
|
#define TEXTMARGINS_Y 4
|
||||||
constexpr int bordWith = 1; //边框宽度,用于快照恢复时的偏移计算
|
constexpr int bordWith = 1; //边框宽度,用于快照恢复时的偏移计算
|
||||||
constexpr int bordHeight = 1; //边框高度,用于快照恢复时的偏移计算
|
constexpr int bordHeight = 1; //边框高度,用于快照恢复时的偏移计算
|
||||||
|
|
||||||
class Button : public Control
|
class Button : public Control
|
||||||
@@ -39,16 +39,16 @@ class Button : public Control
|
|||||||
int padX = TEXTMARGINS_X; // 文本最小左右内边距
|
int padX = TEXTMARGINS_X; // 文本最小左右内边距
|
||||||
int padY = TEXTMARGINS_Y; // 文本最小上下内边距
|
int padY = TEXTMARGINS_Y; // 文本最小上下内边距
|
||||||
|
|
||||||
COLORREF buttonTrueColor; // 按钮被点击后的颜色
|
COLORREF buttonTrueColor; // 按钮被点击后的颜色
|
||||||
COLORREF buttonFalseColor; // 按钮未被点击的颜色
|
COLORREF buttonFalseColor; // 按钮未被点击的颜色
|
||||||
COLORREF buttonHoverColor; // 按钮被鼠标悬停的颜色
|
COLORREF buttonHoverColor; // 按钮被鼠标悬停的颜色
|
||||||
COLORREF buttonBorderColor = RGB(0, 0, 0);// 按钮边框颜色
|
COLORREF buttonBorderColor = RGB(0, 0, 0);// 按钮边框颜色
|
||||||
|
|
||||||
StellarX::ButtonMode mode; // 按钮模式
|
StellarX::ButtonMode mode; // 按钮模式
|
||||||
StellarX::ControlShape shape; // 按钮形状
|
StellarX::ControlShape shape; // 按钮形状
|
||||||
|
|
||||||
StellarX::FillMode buttonFillMode = StellarX::FillMode::Solid; //按钮填充模式
|
StellarX::FillMode buttonFillMode = StellarX::FillMode::Solid; //按钮填充模式
|
||||||
StellarX::FillStyle buttonFillIma = StellarX::FillStyle::BDiagonal; //按钮填充图案
|
StellarX::FillStyle buttonFillIma = StellarX::FillStyle::BDiagonal; //按钮填充图案
|
||||||
IMAGE* buttonFileIMAGE = nullptr; //按钮填充图像
|
IMAGE* buttonFileIMAGE = nullptr; //按钮填充图像
|
||||||
|
|
||||||
std::function<void()> onClickCallback; //回调函数
|
std::function<void()> onClickCallback; //回调函数
|
||||||
@@ -63,20 +63,20 @@ class Button : public Control
|
|||||||
|
|
||||||
// === Tooltip ===
|
// === Tooltip ===
|
||||||
bool tipEnabled = false; // 是否启用
|
bool tipEnabled = false; // 是否启用
|
||||||
bool tipVisible = false; // 当前是否显示
|
bool tipVisible = false; // 当前是否显示
|
||||||
bool tipFollowCursor = false; // 是否跟随鼠标
|
bool tipFollowCursor = false; // 是否跟随鼠标
|
||||||
bool tipUserOverride = false; // 是否用户自定义了tip文本
|
bool tipUserOverride = false; // 是否用户自定义了tip文本
|
||||||
int tipDelayMs = 1000; // 延时(毫秒)
|
int tipDelayMs = 1000; // 延时(毫秒)
|
||||||
int tipOffsetX = 12; // 相对鼠标偏移
|
int tipOffsetX = 12; // 相对鼠标偏移
|
||||||
int tipOffsetY = 18;
|
int tipOffsetY = 18;
|
||||||
ULONGLONG tipHoverTick = 0; // 开始悬停的时间戳
|
ULONGLONG tipHoverTick = 0; // 开始悬停的时间戳
|
||||||
int lastMouseX = 0; // 最新鼠标位置(用于定位)
|
int lastMouseX = 0; // 最新鼠标位置(用于定位)
|
||||||
int lastMouseY = 0;
|
int lastMouseY = 0;
|
||||||
|
|
||||||
std::string tipTextClick; //NORMAL 模式下用
|
std::string tipTextClick; // NORMAL 模式下用
|
||||||
std::string tipTextOn; // click==true 时用
|
std::string tipTextOn; // click==true 时用
|
||||||
std::string tipTextOff; // click==false 时用
|
std::string tipTextOff; // click==false 时用
|
||||||
Label tipLabel; // 直接复用Label作为提示
|
Label tipLabel; // 直接复用Label作为提示
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StellarX::ControlText textStyle; // 按钮文字样式
|
StellarX::ControlText textStyle; // 按钮文字样式
|
||||||
@@ -178,6 +178,7 @@ private:
|
|||||||
bool isMouseInEllipse(int mouseX, int mouseY, int x, int y, int width, int height);
|
bool isMouseInEllipse(int mouseX, int mouseY, int x, int y, int width, int height);
|
||||||
//获取对话框类型
|
//获取对话框类型
|
||||||
bool model() const override { return false; }
|
bool model() const override { return false; }
|
||||||
|
//文本截断
|
||||||
void cutButtonText();
|
void cutButtonText();
|
||||||
// 统一隐藏&恢复背景
|
// 统一隐藏&恢复背景
|
||||||
void hideTooltip();
|
void hideTooltip();
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ namespace StellarX
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @枚举类名称: TextBoxmode
|
* @枚举类名称: TextBoxmode
|
||||||
* @功能描述: 定义了文本框的两种模式
|
* @功能描述: 定义了文本框的三种模式
|
||||||
*
|
*
|
||||||
* @详细说明:
|
* @详细说明:
|
||||||
* 需要限制文本框是否接受用户输入时使用
|
* 需要限制文本框是否接受用户输入时使用
|
||||||
@@ -203,12 +203,13 @@ namespace StellarX
|
|||||||
* @取值说明:
|
* @取值说明:
|
||||||
* INPUT_MODE, // 用户可输入模式
|
* INPUT_MODE, // 用户可输入模式
|
||||||
* READONLY_MODE // 只读模式
|
* READONLY_MODE // 只读模式
|
||||||
|
* PASSWORD_MODE // 密码模式
|
||||||
*/
|
*/
|
||||||
enum class TextBoxmode
|
enum class TextBoxmode
|
||||||
{
|
{
|
||||||
INPUT_MODE, // 用户可输入模式
|
INPUT_MODE, // 用户可输入模式
|
||||||
READONLY_MODE, // 只读模式
|
READONLY_MODE, // 只读模式
|
||||||
PASSWORD_MODE// 密码模式
|
PASSWORD_MODE // 密码模式
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* @文件: StellarX.h
|
* @文件: StellarX.h
|
||||||
* @摘要: 星垣(StellarX) GUI框架 - 主包含头文件
|
* @摘要: 星垣(StellarX) GUI框架 - 主包含头文件
|
||||||
* @版本: v3.0.0
|
* @版本: v3.0.1
|
||||||
* @描述:
|
* @描述:
|
||||||
* 一个为Windows平台打造的轻量级、模块化C++ GUI框架。
|
* 一个为Windows平台打造的轻量级、模块化C++ GUI框架。
|
||||||
* 基于EasyX图形库,提供简洁易用的API和丰富的控件。
|
* 基于EasyX图形库,提供简洁易用的API和丰富的控件。
|
||||||
@@ -12,7 +12,8 @@
|
|||||||
* @作者: 我在人间做废物
|
* @作者: 我在人间做废物
|
||||||
* @邮箱: [3150131407@qq.com] | [ysm3150131407@gmail.com]
|
* @邮箱: [3150131407@qq.com] | [ysm3150131407@gmail.com]
|
||||||
* @官网:https://stellarx-gui.top/
|
* @官网:https://stellarx-gui.top/
|
||||||
* @仓库: [https://github.com/Ysm-04/StellarX]
|
* @仓库: https://github.com/Ysm-04/StellarX
|
||||||
|
* @博客:https://blog.stellarx-gui.top/
|
||||||
*
|
*
|
||||||
* @许可证: MIT License
|
* @许可证: MIT License
|
||||||
* @版权: Copyright (c) 2025 我在人间做废物
|
* @版权: Copyright (c) 2025 我在人间做废物
|
||||||
|
|||||||
@@ -21,13 +21,13 @@
|
|||||||
#include "CoreTypes.h"
|
#include "CoreTypes.h"
|
||||||
#include "Button.h"
|
#include "Button.h"
|
||||||
#include "Canvas.h"
|
#include "Canvas.h"
|
||||||
#define BUTMINHEIGHT 15
|
#define BUTMINHEIGHT 15 //页签按钮最小尺寸,过小会导致显示问题
|
||||||
#define BUTMINWIDTH 30
|
#define BUTMINWIDTH 30 //页签按钮最小尺寸,过小会导致显示问题
|
||||||
class TabControl :public Canvas
|
class TabControl :public Canvas
|
||||||
{
|
{
|
||||||
int tabBarHeight = BUTMINWIDTH; //页签栏高度
|
int tabBarHeight = BUTMINWIDTH; //页签栏高度
|
||||||
bool IsFirstDraw = true; //首次绘制标记
|
bool IsFirstDraw = true; //首次绘制标记
|
||||||
int defaultActivation = -1; //默认激活页签索引
|
int defaultActivation = -1; //默认激活页签索引
|
||||||
StellarX::TabPlacement tabPlacement = StellarX::TabPlacement::Top; //页签排列方式
|
StellarX::TabPlacement tabPlacement = StellarX::TabPlacement::Top; //页签排列方式
|
||||||
std::vector<std::pair<std::unique_ptr<Button>, std::unique_ptr<Canvas>>> controls; //页签/页列表
|
std::vector<std::pair<std::unique_ptr<Button>, std::unique_ptr<Canvas>>> controls; //页签/页列表
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ private:
|
|||||||
using Canvas::addControl; // 禁止外部误用
|
using Canvas::addControl; // 禁止外部误用
|
||||||
void addControl(std::unique_ptr<Control>) = delete; // 精准禁用该重载
|
void addControl(std::unique_ptr<Control>) = delete; // 精准禁用该重载
|
||||||
private:
|
private:
|
||||||
|
// 初始化页签按钮位置和尺寸
|
||||||
inline void initTabBar();
|
inline void initTabBar();
|
||||||
inline void initTabPage();
|
inline void initTabPage();
|
||||||
public:
|
public:
|
||||||
@@ -42,6 +43,7 @@ public:
|
|||||||
TabControl(int x, int y, int width, int height);
|
TabControl(int x, int y, int width, int height);
|
||||||
~TabControl();
|
~TabControl();
|
||||||
|
|
||||||
|
//重写位置设置以适应页签和页面布局
|
||||||
void setX(int x)override;
|
void setX(int x)override;
|
||||||
void setY(int y)override;
|
void setY(int y)override;
|
||||||
|
|
||||||
@@ -67,6 +69,8 @@ public:
|
|||||||
int count() const;
|
int count() const;
|
||||||
//通过页签文本返回索引
|
//通过页签文本返回索引
|
||||||
int indexOf(const std::string& tabText) const;
|
int indexOf(const std::string& tabText) const;
|
||||||
|
//设置脏区并请求重绘
|
||||||
void setDirty(bool dirty) override;
|
void setDirty(bool dirty) override;
|
||||||
|
//请求父控件重绘
|
||||||
void requestRepaint(Control* parent)override;
|
void requestRepaint(Control* parent)override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
* - WM_GETMINMAXINFO:按最小“客户区”换算到“窗口矩形”,提供系统层最小轨迹值。
|
* - WM_GETMINMAXINFO:按最小“客户区”换算到“窗口矩形”,提供系统层最小轨迹值。
|
||||||
* - runEventLoop:只记录 WM_SIZE 的新尺寸;真正绘制放在 needResizeDirty 时集中处理。
|
* - runEventLoop:只记录 WM_SIZE 的新尺寸;真正绘制放在 needResizeDirty 时集中处理。
|
||||||
*/
|
*/
|
||||||
//fuck windows fuck win32
|
//fuck windows
|
||||||
|
//fuck win32
|
||||||
|
//fuck xiaomi
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Control.h"
|
#include "Control.h"
|
||||||
@@ -55,6 +57,7 @@ class Window
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
bool dialogClose = false; // 项目内使用的状态位,对话框关闭标志
|
bool dialogClose = false; // 项目内使用的状态位,对话框关闭标志
|
||||||
|
mutable bool dialogOpen = false; // 项目内使用的状态位,对话框打开标志
|
||||||
|
|
||||||
// —— 构造/析构 ——(仅初始化成员;实际样式与子类化在 draw() 中完成)
|
// —— 构造/析构 ——(仅初始化成员;实际样式与子类化在 draw() 中完成)
|
||||||
Window(int width, int height, int mode);
|
Window(int width, int height, int mode);
|
||||||
|
|||||||
+2
-1
@@ -313,6 +313,7 @@ bool Button::handleEvent(const ExMessage& msg)
|
|||||||
if (hover != oldHover)
|
if (hover != oldHover)
|
||||||
{
|
{
|
||||||
SX_LOGD("Button") << SX_T("悬停变化: ","hover change: ") << "id=" << id
|
SX_LOGD("Button") << SX_T("悬停变化: ","hover change: ") << "id=" << id
|
||||||
|
<< " text= " << text
|
||||||
<< " " << (oldHover ? 1 : 0) << "->" << (hover ? 1 : 0);
|
<< " " << (oldHover ? 1 : 0) << "->" << (hover ? 1 : 0);
|
||||||
}
|
}
|
||||||
// 处理鼠标点击事件
|
// 处理鼠标点击事件
|
||||||
@@ -321,7 +322,7 @@ bool Button::handleEvent(const ExMessage& msg)
|
|||||||
if (mode == StellarX::ButtonMode::NORMAL)
|
if (mode == StellarX::ButtonMode::NORMAL)
|
||||||
{
|
{
|
||||||
click = true;
|
click = true;
|
||||||
SX_LOGD("Button") << SX_T("被点击: ","lbtn - down:")<< "id = " << id << " mode = " << (int)mode;
|
SX_LOGD("Button") << SX_T("被点击: ","lbtn - down:")<< "id = " << id <<" text = "<<text << " mode = " << (int)mode;
|
||||||
|
|
||||||
dirty = true;
|
dirty = true;
|
||||||
consume = true;
|
consume = true;
|
||||||
|
|||||||
+8
-3
@@ -80,12 +80,12 @@ bool Dialog::handleEvent(const ExMessage& msg)
|
|||||||
// 如果正在清理或标记为待清理,则不处理事件
|
// 如果正在清理或标记为待清理,则不处理事件
|
||||||
if (pendingCleanup || isCleaning)
|
if (pendingCleanup || isCleaning)
|
||||||
return false;
|
return false;
|
||||||
|
// 模态对话框不允许点击外部区域
|
||||||
// 模态对话框:点击对话框外部区域时,发出提示音(\a)并吞噬该事件,不允许操作背景内容。
|
// 模态对话框:点击对话框外部区域时,发出提示音(\a)并吞噬该事件,不允许操作背景内容。
|
||||||
if (modal && msg.message == WM_LBUTTONUP &&
|
if (modal && msg.message == WM_LBUTTONUP &&
|
||||||
(msg.x < x || msg.x > x + width || msg.y < y || msg.y > y + height))
|
(msg.x < x || msg.x > x + width || msg.y < y || msg.y > y + height))
|
||||||
{
|
{
|
||||||
std::cout << "\a" << std::endl;
|
std::cout << "\a" << std::endl;
|
||||||
// 模态对话框不允许点击外部区域
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +157,8 @@ void Dialog::Show()
|
|||||||
close = false;
|
close = false;
|
||||||
shouldClose = false;
|
shouldClose = false;
|
||||||
|
|
||||||
|
hWnd.dialogOpen = true;// 通知窗口有对话框打开
|
||||||
|
|
||||||
if (modal)
|
if (modal)
|
||||||
{
|
{
|
||||||
// 模态对话框需要阻塞当前线程直到对话框关闭
|
// 模态对话框需要阻塞当前线程直到对话框关闭
|
||||||
@@ -193,7 +195,7 @@ void Dialog::Show()
|
|||||||
setDirty(true);
|
setDirty(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ② 处理这只对话框的鼠标/键盘(沿用你原来 EX_MOUSE | EX_KEY)
|
// ② 处理这只对话框的鼠标/键盘(沿用原来 EX_MOUSE | EX_KEY)
|
||||||
ExMessage msg;
|
ExMessage msg;
|
||||||
if (peekmessage(&msg, EX_MOUSE | EX_KEY))
|
if (peekmessage(&msg, EX_MOUSE | EX_KEY))
|
||||||
{
|
{
|
||||||
@@ -274,6 +276,7 @@ void Dialog::initButtons()
|
|||||||
okbutton->setOnClickListener([this]()
|
okbutton->setOnClickListener([this]()
|
||||||
{
|
{
|
||||||
this->SetResult(StellarX::MessageBoxResult::OK);
|
this->SetResult(StellarX::MessageBoxResult::OK);
|
||||||
|
this->hWnd.dialogClose = true;
|
||||||
this->Close(); });
|
this->Close(); });
|
||||||
|
|
||||||
okbutton->textStyle = this->textStyle;
|
okbutton->textStyle = this->textStyle;
|
||||||
@@ -540,6 +543,8 @@ void Dialog::getTextSize()
|
|||||||
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
||||||
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
||||||
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
|
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
|
||||||
|
int tempHeight = 0;
|
||||||
|
int tempWidth = 0;
|
||||||
for (auto& text : lines)
|
for (auto& text : lines)
|
||||||
{
|
{
|
||||||
int w = textwidth(LPCTSTR(text.c_str()));
|
int w = textwidth(LPCTSTR(text.c_str()));
|
||||||
@@ -549,6 +554,7 @@ void Dialog::getTextSize()
|
|||||||
if (this->textWidth < w)
|
if (this->textWidth < w)
|
||||||
this->textWidth = w;
|
this->textWidth = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreStyle();
|
restoreStyle();
|
||||||
}
|
}
|
||||||
// 计算逻辑:对话框宽度取【文本区域最大宽度】和【按钮区域总宽度】中的较大值。
|
// 计算逻辑:对话框宽度取【文本区域最大宽度】和【按钮区域总宽度】中的较大值。
|
||||||
@@ -651,7 +657,6 @@ void Dialog::performDelayedCleanup()
|
|||||||
// 所有普通控件
|
// 所有普通控件
|
||||||
for (auto& c : hWnd.getControls()) c->draw();
|
for (auto& c : hWnd.getControls()) c->draw();
|
||||||
// 其他对话框(this 已经 show=false,会早退不绘)
|
// 其他对话框(this 已经 show=false,会早退不绘)
|
||||||
// 注意:此处若有容器管理,需要按你的现状遍历 dialogs 再 draw
|
|
||||||
EndBatchDraw();
|
EndBatchDraw();
|
||||||
FlushBatchDraw();
|
FlushBatchDraw();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-4
@@ -117,15 +117,12 @@ namespace StellarX
|
|||||||
// - 只影响终端解释输出字节的方式,不影响源码文件编码
|
// - 只影响终端解释输出字节的方式,不影响源码文件编码
|
||||||
// - 使用 once_flag 避免重复 system 调用造成噪声与性能浪费
|
// - 使用 once_flag 避免重复 system 调用造成噪声与性能浪费
|
||||||
//
|
//
|
||||||
// 注意:
|
|
||||||
// - 下面原注释写“切到 UTF-8”,但实际命令是 chcp 936(GBK)
|
|
||||||
// - 为避免改动你原注释,这里补充说明事实,保持行为不变
|
|
||||||
void SxLogger::setGBK()
|
void SxLogger::setGBK()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
std::call_once(once, []() {
|
std::call_once(once, []() {
|
||||||
// 切到 UTF-8,避免中文日志在 CP936 控制台下乱码
|
// 切到chcp 936(GBK),避免中文日志在 CP936 控制台下乱码
|
||||||
// 说明:这不是 WinAPI;是执行系统命令
|
// 说明:这不是 WinAPI;是执行系统命令
|
||||||
std::system("chcp 936 >nul");
|
std::system("chcp 936 >nul");
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -368,7 +368,7 @@ int TabControl::getActiveIndex() const
|
|||||||
if (c.first->isClicked())
|
if (c.first->isClicked())
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
return idx;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabControl::setActiveIndex(int idx)
|
void TabControl::setActiveIndex(int idx)
|
||||||
|
|||||||
+48
-13
@@ -29,19 +29,44 @@ void TextBox::draw()
|
|||||||
int text_width = 0;
|
int text_width = 0;
|
||||||
int text_height = 0;
|
int text_height = 0;
|
||||||
std::string pwdText;
|
std::string pwdText;
|
||||||
|
std::string displayText; // 用于显示的文本(可能被截断)
|
||||||
|
bool isTextTruncated = false; // 标记文本是否被截断
|
||||||
|
|
||||||
if (StellarX::TextBoxmode::PASSWORD_MODE == mode)
|
if (StellarX::TextBoxmode::PASSWORD_MODE == mode)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < text.size(); ++i)
|
for (size_t i = 0; i < text.size(); ++i)
|
||||||
pwdText += '*';
|
pwdText += '*';
|
||||||
text_width = textwidth(LPCTSTR(pwdText.c_str()));
|
displayText = pwdText;
|
||||||
text_height = textheight(LPCTSTR(pwdText.c_str()));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
text_width = textwidth(LPCTSTR(text.c_str()));
|
displayText = text;
|
||||||
text_height = textheight(LPCTSTR(text.c_str()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算可用宽度(留出左右边距)
|
||||||
|
int availableWidth = width - 20; // 左右各10像素边距
|
||||||
|
|
||||||
|
// 截断文本以适应可用宽度
|
||||||
|
int currentWidth = textwidth(LPCTSTR(displayText.c_str()));
|
||||||
|
if (currentWidth > availableWidth && availableWidth > 0)
|
||||||
|
{
|
||||||
|
// 需要截断文本,预留空间放置省略号
|
||||||
|
int ellipsisWidth = textwidth("...");
|
||||||
|
int truncatedWidth = availableWidth - ellipsisWidth;
|
||||||
|
|
||||||
|
std::string truncatedText = displayText;
|
||||||
|
while (truncatedText.size() > 0 && textwidth(LPCTSTR(truncatedText.c_str())) > truncatedWidth)
|
||||||
|
{
|
||||||
|
truncatedText.pop_back();
|
||||||
|
}
|
||||||
|
displayText = truncatedText + "...";
|
||||||
|
isTextTruncated = true;
|
||||||
|
currentWidth = textwidth(LPCTSTR(displayText.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
text_width = currentWidth;
|
||||||
|
text_height = textheight(LPCTSTR(displayText.c_str()));
|
||||||
|
|
||||||
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
|
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
|
||||||
saveBackground(this->x, this->y, this->width, this->height);
|
saveBackground(this->x, this->y, this->width, this->height);
|
||||||
// 恢复背景(清除旧内容)
|
// 恢复背景(清除旧内容)
|
||||||
@@ -51,23 +76,19 @@ void TextBox::draw()
|
|||||||
{
|
{
|
||||||
case StellarX::ControlShape::RECTANGLE:
|
case StellarX::ControlShape::RECTANGLE:
|
||||||
fillrectangle(x, y, x + width, y + height);//有边框填充矩形
|
fillrectangle(x, y, x + width, y + height);//有边框填充矩形
|
||||||
StellarX::TextBoxmode::PASSWORD_MODE == mode ? outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(pwdText.c_str()))
|
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(displayText.c_str()));
|
||||||
: outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str()));
|
|
||||||
break;
|
break;
|
||||||
case StellarX::ControlShape::B_RECTANGLE:
|
case StellarX::ControlShape::B_RECTANGLE:
|
||||||
solidrectangle(x, y, x + width, y + height);//无边框填充矩形
|
solidrectangle(x, y, x + width, y + height);//无边框填充矩形
|
||||||
StellarX::TextBoxmode::PASSWORD_MODE == mode ? outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(pwdText.c_str()))
|
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(displayText.c_str()));
|
||||||
: outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str()));
|
|
||||||
break;
|
break;
|
||||||
case StellarX::ControlShape::ROUND_RECTANGLE:
|
case StellarX::ControlShape::ROUND_RECTANGLE:
|
||||||
fillroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//有边框填充圆角矩形
|
fillroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//有边框填充圆角矩形
|
||||||
StellarX::TextBoxmode::PASSWORD_MODE == mode ? outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(pwdText.c_str()))
|
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(displayText.c_str()));
|
||||||
:outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str()));
|
|
||||||
break;
|
break;
|
||||||
case StellarX::ControlShape::B_ROUND_RECTANGLE:
|
case StellarX::ControlShape::B_ROUND_RECTANGLE:
|
||||||
solidroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//无边框填充圆角矩形
|
solidroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//无边框填充圆角矩形
|
||||||
StellarX::TextBoxmode::PASSWORD_MODE == mode ? outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(pwdText.c_str()))
|
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(displayText.c_str()));
|
||||||
:outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str()));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
restoreStyle();
|
restoreStyle();
|
||||||
@@ -197,10 +218,24 @@ void TextBox::setTextBoxBk(COLORREF color)
|
|||||||
|
|
||||||
void TextBox::setText(std::string text)
|
void TextBox::setText(std::string text)
|
||||||
{
|
{
|
||||||
|
if(text == this->text)
|
||||||
|
return; // 文本未改变,无需更新和重绘
|
||||||
if (text.size() > maxCharLen)
|
if (text.size() > maxCharLen)
|
||||||
text = text.substr(0, maxCharLen);
|
text = text.substr(0, maxCharLen);
|
||||||
this->text = text;
|
this->text = text;
|
||||||
this->dirty = true;
|
this->dirty = true; // 标记需要重绘,不论是否窗口图形上下文是否已初始化,等第一次绘制时由窗口真正调用 draw() 来重绘显示文本
|
||||||
|
|
||||||
|
//有父控件时请求父控件重绘,无父控件时直接重绘,确保文本更新后界面正确刷新显示
|
||||||
|
if (nullptr != parent)
|
||||||
|
{
|
||||||
|
//通过hasSnap是否持有有效快照,判断控件是否已经绘制过,避免在控件未绘制前/窗口图形上下文未初始化调用draw()导致的错误
|
||||||
|
if (hasSnap)
|
||||||
|
requestRepaint(parent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (hasSnap)
|
||||||
|
draw();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TextBox::getText() const
|
std::string TextBox::getText() const
|
||||||
|
|||||||
+53
-25
@@ -193,9 +193,10 @@ LRESULT CALLBACK Window::WndProcThunk(HWND h, UINT m, WPARAM w, LPARAM l)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyMinSizeOnSizing(prc, w, h, self->minClientW, self->minClientH);
|
|
||||||
RECT before = *prc;// 记录调整前矩形以便日志输出
|
RECT before = *prc;// 记录调整前矩形以便日志输出
|
||||||
if (before.left != prc->left || before.top != prc->top || before.right != prc->right || before.bottom != prc->bottom)
|
ApplyMinSizeOnSizing(prc, w, h, self->minClientW, self->minClientH);
|
||||||
|
//if (before.left != prc->left || before.top != prc->top || before.right != prc->right || before.bottom != prc->bottom)
|
||||||
|
if (memcmp(&before, prc, sizeof(RECT)) != 0)
|
||||||
{
|
{
|
||||||
SX_LOGD("Resize")
|
SX_LOGD("Resize")
|
||||||
<< SX_T("WM_SIZING 夹具:","WM_SIZING clamp: ")
|
<< SX_T("WM_SIZING 夹具:","WM_SIZING clamp: ")
|
||||||
@@ -372,7 +373,8 @@ int Window::runEventLoop()
|
|||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
|
|
||||||
bool consume = false;
|
bool consume = false; // 事件是否被消费的标志(用于输入事件分发)
|
||||||
|
bool redrawDialogs = false; // 是否需要重绘对话框(控件事件可能引起对话框状态变化)
|
||||||
|
|
||||||
if (peekmessage(&msg, EX_MOUSE | EX_KEY | EX_WINDOW, true))
|
if (peekmessage(&msg, EX_MOUSE | EX_KEY | EX_WINDOW, true))
|
||||||
{
|
{
|
||||||
@@ -422,9 +424,7 @@ int Window::runEventLoop()
|
|||||||
{
|
{
|
||||||
auto& d = *it;
|
auto& d = *it;
|
||||||
if (d->IsVisible() && !d->model())
|
if (d->IsVisible() && !d->model())
|
||||||
{
|
|
||||||
consume = d->handleEvent(msg);
|
consume = d->handleEvent(msg);
|
||||||
}
|
|
||||||
if (consume)
|
if (consume)
|
||||||
{
|
{
|
||||||
SX_LOGD("Event") << SX_T("事件被非模态对话框处理","Event consumed by non-modal dialog");
|
SX_LOGD("Event") << SX_T("事件被非模态对话框处理","Event consumed by non-modal dialog");
|
||||||
@@ -433,44 +433,66 @@ int Window::runEventLoop()
|
|||||||
}
|
}
|
||||||
if (!consume)
|
if (!consume)
|
||||||
{
|
{
|
||||||
for (auto& c : controls)
|
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
|
||||||
{
|
{
|
||||||
consume = c->handleEvent(msg);
|
consume = (*it)->handleEvent(msg);
|
||||||
if (consume)
|
if (consume)
|
||||||
{
|
{
|
||||||
SX_LOGD("Event") << SX_T("事件被控件处理 id=","Event consumed by control id=") << c->getId();
|
SX_LOGD("Event") << SX_T("事件被控件处理 id=", "Event consumed by control id=") << (*it)->getId();
|
||||||
|
redrawDialogs = true; // 控件事件可能引起对话框状态变化,标记需要重绘对话框
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 关键点⑦:事件处理后,如果对话框状态可能变化(例如控件事件引起的控件重绘可能导致对话框被覆盖),优先重绘对话框以确保界面响应及时;后续再根据 needResizeDirty 进行一次性重绘。
|
||||||
|
if (redrawDialogs)
|
||||||
|
{
|
||||||
|
for (auto& d : dialogs)
|
||||||
|
{
|
||||||
|
if (!d->model() && d->IsVisible())
|
||||||
|
d->setDirty(true);
|
||||||
|
d->draw();
|
||||||
|
}
|
||||||
|
redrawDialogs = false; // 重置标志
|
||||||
|
}
|
||||||
|
|
||||||
//如果有对话框打开或者关闭强制重绘
|
//如果有对话框打开或者关闭强制重绘
|
||||||
bool needredraw = false;
|
bool needredraw = false;
|
||||||
for (auto& d : dialogs)
|
if(dialogOpen)
|
||||||
{
|
{
|
||||||
needredraw = d->IsVisible();
|
for (auto& d : dialogs)
|
||||||
if (needredraw)break;
|
{
|
||||||
|
needredraw = d->IsVisible();
|
||||||
|
if (needredraw)break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (needredraw || dialogClose)
|
if (needredraw || dialogClose)
|
||||||
{
|
{
|
||||||
// 对话框关闭后,需要手动合成一个鼠标移动消息并分发给所有普通控件,
|
if (dialogClose)
|
||||||
// 以便它们能及时更新悬停状态(hover),否则悬停状态可能保持错误状态。
|
|
||||||
// 先把当前鼠标位置转换为客户区坐标,并合成一次 WM_MOUSEMOVE,先分发给控件更新 hover 状态
|
|
||||||
POINT pt;
|
|
||||||
if (GetCursorPos(&pt))
|
|
||||||
{
|
{
|
||||||
ScreenToClient(this->hWnd, &pt);
|
// 对话框关闭后,需要手动合成一个鼠标移动消息并分发给所有普通控件,
|
||||||
ExMessage mm;
|
// 以便它们能及时更新悬停状态(hover),否则悬停状态可能保持错误状态。
|
||||||
mm.message = WM_MOUSEMOVE;
|
// 先把当前鼠标位置转换为客户区坐标,并合成一次 WM_MOUSEMOVE,先分发给控件更新 hover 状态
|
||||||
mm.x = (short)pt.x;
|
SX_LOGD("Event") << SX_T("对话框关闭,合成WM_MOUSEMOVE已下发", "Dialog closed; synthetic WM_MOUSEMOVE dispatched");
|
||||||
mm.y = (short)pt.y;
|
POINT pt;
|
||||||
// 只分发给 window 层控件(因为 dialog 已经关闭或即将关闭)
|
if (GetCursorPos(&pt))
|
||||||
for (auto& c : controls)
|
{
|
||||||
c->handleEvent(mm);
|
ScreenToClient(this->hWnd, &pt);
|
||||||
|
ExMessage mm;
|
||||||
|
mm.message = WM_MOUSEMOVE;
|
||||||
|
mm.x = (short)pt.x;
|
||||||
|
mm.y = (short)pt.y;
|
||||||
|
// 只分发给 window 层控件(因为 dialog 已经关闭或即将关闭)
|
||||||
|
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
|
||||||
|
(*it)->handleEvent(mm);
|
||||||
|
}
|
||||||
|
dialogClose = false; // 重置标志
|
||||||
}
|
}
|
||||||
|
|
||||||
BeginBatchDraw();
|
BeginBatchDraw();
|
||||||
|
SX_LOGD("Event") << SX_T("对话框打开/关闭,触发全量重绘", "The dialog box opens/closes, triggering a full redraw");
|
||||||
// 先绘制普通控件
|
// 先绘制普通控件
|
||||||
for (auto& c : controls)
|
for (auto& c : controls)
|
||||||
c->draw();
|
c->draw();
|
||||||
@@ -483,6 +505,8 @@ int Window::runEventLoop()
|
|||||||
}
|
}
|
||||||
EndBatchDraw();
|
EndBatchDraw();
|
||||||
needredraw = false;
|
needredraw = false;
|
||||||
|
dialogOpen = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
// —— 统一收口(needResizeDirty 为真时执行一次性重绘)——
|
// —— 统一收口(needResizeDirty 为真时执行一次性重绘)——
|
||||||
if (needResizeDirty)
|
if (needResizeDirty)
|
||||||
@@ -655,7 +679,10 @@ bool Window::hasNonModalDialogWithCaption(const std::string& caption, const std:
|
|||||||
if (!dptr) continue;
|
if (!dptr) continue;
|
||||||
if (auto* d = dynamic_cast<Dialog*>(dptr.get()))
|
if (auto* d = dynamic_cast<Dialog*>(dptr.get()))
|
||||||
if (d->IsVisible() && !d->model() && d->GetCaption() == caption && d->GetText() == message)
|
if (d->IsVisible() && !d->model() && d->GetCaption() == caption && d->GetText() == message)
|
||||||
|
{
|
||||||
|
dialogOpen = true;
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -853,4 +880,5 @@ void Window::adaptiveLayout(std::unique_ptr<Control>& c, const int finalH, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c->onWindowResize();
|
c->onWindowResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user