初版-带一个进度条

This commit is contained in:
睿 安
2026-01-24 22:42:46 +08:00
commit c367128889
100 changed files with 19726 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
build/*
build64/*
demo/*

51
CLAUDE.md Normal file
View File

@@ -0,0 +1,51 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
EzUI 是一个基于原生 Win32 消息机制和 Direct2D 的 C++ 桌面 UI 框架,提供类似 Web 前端的 CSS 样式系统和弹性布局。
## 构建命令
```bash
build.bat # 构建 32 位版本
build64.bat # 构建 64 位版本
```
或使用 CMake 直接构建:
```bash
cmake -B build
cmake --build build
```
## 核心架构
### 窗口类型 (继承层次)
- `Window` - 经典边框窗口
- `BorderlessWindow` - 无边框带阴影窗口
- `LayeredWindow` - 分层透明窗口,支持异形
- `PopupWindow` - 失焦自动关闭的弹出窗口
### 控件系统
所有控件继承自 `Control` 基类,核心控件包括:
- 基础控件:`Label`, `Button`, `TextBox`, `PictureBox`
- 选择控件:`CheckBox`, `RadioButton`, `ComboBox`
- 布局容器:`HLayout`, `VLayout`, `HListView`, `VListView`, `TileListView`, `TabLayout`
- 功能控件:`ScrollBar`, `Menu`, `NotifyIcon`, `ProgressBar`
### 样式与渲染
- `UIManager` - UI 样式与资源管理,支持 XML 布局加载
- `UISelector` - CSS 选择器匹配系统
- `Direct2DRender` - Direct2D 绘图实现
- `RenderTypes` - 颜色、对齐方式等绘图类型
### 事件系统
支持事件冒泡机制可实现事件捕获与穿透。Debug 模式下按 F11 可查看布局信息和控件边界。
## 开发约定
- 头文件位于 `include/EzUI/` 目录
- 源文件位于 `sources/` 目录
- 一切皆控件,纯代码组合 UI
- 使用 CSS 驱动视觉,结构与样式分离

47
CMakeLists.txt Normal file
View File

@@ -0,0 +1,47 @@
cmake_minimum_required(VERSION 3.0...9.0)
if(UNIX)
add_compile_options(-finput-charset=UTF-8)
add_compile_options(-fexec-charset=UTF-8)
elseif(WIN32)
add_compile_options(/utf-8)
endif()
set(tag $<IF:$<CONFIG:Debug>,Debug,Release>)
#让所有项目都使用UNICODE
add_definitions(-D_UNICODE -DUNICODE)
project(EzUI)
file(GLOB src ./include/EzUI/*.* ./sources/*.* )
# 添加一个选项,供用户选择构建静态库或动态库
option(BUILD_SHARED_LIBS "Build shared library instead of static library" OFF)
if(BUILD_SHARED_LIBS)
add_library(EzUI SHARED ${src})
else()
add_library(EzUI STATIC ${src})
target_compile_definitions(EzUI PUBLIC EZUI_STATIC)
endif()
target_include_directories(EzUI PRIVATE ./include/EzUI)
set_target_properties(EzUI PROPERTIES LINKER_LANGUAGE CXX)
# 定义函数: 添加资源打包任务并关联到指定项目
function(add_resource_package TARGET_NAME INPUT_DIR OUTPUT_FILE)
set (ProjectName ${TARGET_NAME}_EzUI_ResourcePackager)
# 定义始终运行的伪目标
add_custom_target(
${ProjectName} # 更简洁的目标命名
COMMAND ${CMAKE_SOURCE_DIR}/ResPackage.exe -package ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${OUTPUT_FILE}
COMMENT "Packaging resources for ${TARGET_NAME}..."
)
# 将目标归类到专用文件夹
set_target_properties(${ProjectName} PROPERTIES
FOLDER "CMakePredefinedTargets" # 在VS中归类到指定文件夹
)
# 主目标依赖伪目标
add_dependencies(${TARGET_NAME} ${ProjectName})
endfunction()
#添加子项目
#add_subdirectory(./demo) # 暂时注释掉,只编译库

199
README.md Normal file
View File

@@ -0,0 +1,199 @@
# EzUI
**EzUI** 是一个基于原生 Win32 消息机制和 Direct2D 的高性能桌面 UI 框架,具备强大的弹性布局、伪类样式支持和控件系统,目标是提供如 Web 前端般灵活、直观的界面开发体验。
> 🚀 支持高 DPI、分层窗口、响应式布局、CSS 风格皮肤、GIF 动画、控件组合与继承机制。未来将支持跨平台 2D 图形库扩展。
---
## ✨ 框架特色
- 🪱 **基于 Win32 消息机制**:轻量、无依赖,逻辑控件系统拦截并下发鼠标键盘消息
- 🎨 **2D 图形绘制**:当前基于 Direct2D 绘图,具备高性能、高分辨率适配能力。
- 🧹 **弹性布局系统**:支持自动宽高、自适应布局,开发体验类比前端 Flex 设计。
- 🎭 **伪类 CSS 支持**:支持 `hover``active``checked` 等状态样式类选择器、ID 选择器、组合选择器等完整选择器系统。
- 🌟 **控件组合系统**Label、Button、TextBox、CheckBox、RadioButton 等丰富控件可自由组合构建 UI。
- 💡 **事件系统**:支持事件冒泡机制,可实现事件捕获与穿透。
- 🧩 **高 DPI 适配**:框架自动处理 DPI 缩放与坐标换算,支持多显示器高分屏。
- 🪪 **多种窗口类型支持**
- `Window`:经典边框窗口 windows自动发送`WM_PAINT`消息绘制
- `BorderlessWindow`:无边框、带阴影 windows自动发送`WM_PAINT`消息绘制
- `LayeredWindow`:分层透明窗口,支持异形与实时重绘 手动发送 `WM_PAINT`消息进行绘制
- `PopupWindow`:失焦自动关闭的弹出窗口,适用于菜单等
---
## 📆 快速开始
### 1. 下载源码
```bash
git clone https://github.com/NewYoungCode/EzUI.git
```
### 2. 安装 CMake
请前往 [CMake 官网](https://cmake.org/download/) 下载安装。
### 3. 构建与运行
- 构建 32 位项目:
```bash
build.bat
```
- 构建 64 位项目:
```bash
build64.bat
```
---
## 📀 使用说明
### 🏁 程序入口
请在 `WinMain` 函数中创建 `Application` 实例并调用 `app.Exec()` 以启动消息循环:
```cpp
#include <Windows.h>
#include "EzUI/UIManager.h"
#include "Ezui/Window.h"
#include "Ezui/Application.h"
using namespace ezui;
class TestForm :public Window {
private:
UIManager umg;//ui管理器
public:
TestForm() :Window(800, 600) {
//umg.LoadXmlFile("res/form.htm");//从文件中加载xml界面
umg.LoadXmlData("<vbox> <label text=\"hello world\"></label> </vbox>");//从内存中加载布局
umg.SetupUI(this);//设置ui
}
virtual ~TestForm() {
}
virtual void OnClose(bool& bClose) override {
int result = ::MessageBoxW(frm.Hwnd(), L"要退出程序吗?", L"提示", MB_YESNO | MB_ICONQUESTION);
if (result == IDYES) {
Application::Exit(0);//当窗口关闭时 整个程序退出
} else {
bClose=false; // 用户点击了“否” 此标志设置为false将不会关闭
}
}
};
//程序入口;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
Application app;
TestForm testForm;
testForm.SetText(L"我是测试窗口");
testForm.Show(nCmdShow);
return app.Exec();
};
```
### 🧱 控件与组件列表(部分)
- `Application`EzUI 应用入口与消息循环控制器
- `Window`:经典 Win32 边框窗口
- `BorderlessWindow`:无边框带阴影窗口
- `LayeredWindow`:支持透明和异形的分层窗口
- `PopupWindow`:失焦自动关闭的弹出窗口
- `Control`:所有控件基础类,封装鼠标命中、事件分发和重绘逻辑
- `Label`:文字标签控件
- `Button`:标准按钮控件
- `TextBox`:文本输入框
- `CheckBox` / `RadioButton`:复选框 / 单选框控件
- `PictureBox`:图片显示控件,支持 GIF
- `ComboBox`:下拉框控件
- `Menu`:菜单支持
- `NotifyIcon`:托盘图标支持
- `ScrollBar` / `HScrollBar` / `VScrollBar`:滚动条组件
- `HLayout` / `VLayout`:水平/垂直布局容器
- `HListView` / `VListView`:横向/纵向列表视图容器
- `TileListView`:瓦片式列表视图
- `TabLayout`:选项卡切换容器
- `Spacer`:空隙组件,占位用
- `ShadowBox`:为无边框窗口提供窗口阴影
- `IFrame`:内嵌页面控件,功能类似 Web 前端中的 `<iframe>`
- `UIDef`:框架内使用的宏定义集合
- `UIManager`UI 样式与资源统一管理
- `UISelector`:选择器匹配系统,实现类选择器、链式查询控件
- `UIString` / `tinystr.h` / `tinyxml.h`:字符串处理与 XML 配置解析支持
- `RenderTypes`:绘图相关类型定义(颜色、对齐方式等)
- `Direct2DRender`Direct2D 绘图实现类
- `Bitmap`:图像资源封装
- `Resource.h`:自定义资源管理类(非 VS rc 文件),用于手动加载与管理框架资源
- `Timer` / `Task`:定时与异步任务处理辅助类
- `EzUI.h`:框架主接口头文件,定义事件类、枚举、全局资源等核心结构
- `Animation.h`类似于QT的那种过渡动画
### 🎨 样式示例
```css
#submitBtn {
width: 100px; /*静态样式时支持调整大小*/
height: 30px; /*静态样式时支持调整大小*/
background-color: #0078d7;
}
#submitBtn:hover {
background-color: #005a9e;
}
.classA .classB { /*多选择器*/
color:#ffffff;
}
label{ /*标签选择器*/
color:#ffffff;
}
.check:checked {
border:1px;
border-radius:10px;
border-color: #005a9e;
}
```
---
## 🔧 调试技巧
- 在 Debug 模式下运行时,按下 `F11` 可实时查看布局信息,高亮显示控件边界。
---
## 🧬 开发理念
- **“一切皆控件”**:无 UI 描述文件,纯代码式组件组合;
- **“CSS 驱动视觉”**:结构和样式分离;
- **“面向前端思维”**:布局、交互风格向 Web 靠拟;
- **“跨平台可拟扩”**:绘图模块可替换(未来可能会使用跨平台图形库);
---
## 📖 学习 & 技术支持
- 视频教程https://space.bilibili.com/240269358/video
- QQ718987717
- QQ群758485934
- 邮箱:[19980103ly@gmail.com]/[19980103ly@gmail.com]
- 微信wx19980103yon
---
## 📄 License

BIN
ResPackage.exe Normal file

Binary file not shown.

29
include/EzUI/Animation.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include "EzUI.h"
#include "Timer.h"
namespace ezui {
class UI_EXPORT Animation : public Object {
private:
Timer *m_timer;
float m_startValue = 0;
float m_endValue = 0;
float m_currValue = 0;
float m_speedPerMs = 0;
std::chrono::steady_clock::time_point m_lastTime;
public:
//当值更改的时候发生的事件(请绑定此函数进行回调,请自行线程同步)
std::function<void(float)> ValueChanged;
Animation(Object* parentObject = NULL);
virtual ~Animation();
void SetStartValue(float value);
void SetEndValue(float value);
//开始动画
void Start(int durationMs, int fps = 90);
//动画是否已经停止
bool IsStopped();
void Stop();
};
};

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Window.h"
#include "Resource.h"
namespace ezui {
class UI_EXPORT Application
{
public:
//退出消息循环
static void Exit(int exitCode = 0);
//获取程序启动路径
static UIString StartPath();
public:
Application(HINSTANCE hInstance = NULL);
//使用本地文件名称或者资源中的名称加载资源包
//填入vs中的资源ID名称 或者 本地文件名 一个Application只允许有一个资源文件
bool SetResource(const UIString& localOrResName);
//启用高DPI适配
void EnableHighDpi();
virtual ~Application();
//执行消息循环
int Exec();
};
};

34
include/EzUI/Bitmap.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
#include "EzUI.h"
namespace ezui {
//BGRA 32位图
class UI_EXPORT Bitmap {
private:
int m_width = 0;
int m_height = 0;
HBITMAP m_bmp = NULL;
HDC m_hdc = NULL;
byte* m_point = NULL;
BITMAPINFO m_bmpInfo;
Bitmap(const Bitmap& hBitmap) = delete;
void operator=(const Bitmap& hBitmap) = delete;
protected:
void Create(int width, int height);
public:
int Width()const;
int Height()const;
//BGRA 32位图
Bitmap(int width, int height);
Bitmap(HDC dc, const Rect& rect);
void SetPixel(int x, int y, const Color& color);
Color GetPixel(int x, int y)const;
byte* GetPixel();
void Earse(const Rect& rect);//抹除矩形内容
HBITMAP GetHBITMAP();
HDC GetDC();
bool Save(const UIString& fileName);
Bitmap* Clone()const;
virtual ~Bitmap();
};
};

View File

@@ -0,0 +1,38 @@
#pragma once
#include "Window.h"
#include "ShadowBox.h"
namespace ezui {
/// <summary>
/// BorderlessWindow //无边框 带阴影
/// </summary>
class UI_EXPORT BorderlessWindow :public Window {
private:
int m_shadowWeight = 20;
ShadowBox* m_shadowBox = NULL;
float m_shadowScale = 1.0f;
//是否支持缩放
bool m_bResize = false;
protected:
virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)override;
virtual void OnMove(const Point& location) override;
virtual void OnSize(const Size& sz) override;
virtual void OnDpiChange(float systemScale, const Rect& newRect);//当dpi发生更改时
virtual HWND GetShadowHwnd()override;//获取阴影窗口句柄
public:
//设置阴影宽度
void SetShadow(int weight);
BorderlessWindow(int width, int height, HWND owner = NULL, DWORD dwStyle = NULL, DWORD dwExStyle = NULL);
virtual ~BorderlessWindow();
//更新窗口阴影
void UpdateShadowBox();
//获取阴影窗口
ShadowBox* GetShadowBox();
//关闭窗口阴影 关掉阴影窗口 已有的边框也会随之消失
void CloseShadowBox();
//设置窗口缩放支持
void SetResizable(bool resize);
//是否支持调整大小
bool IsResizable();
};
};

14
include/EzUI/Button.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include "Label.h"
namespace ezui {
class UI_EXPORT Button :
public Label
{
private:
void Init();
public:
Button(Object* parentObject = NULL);
virtual ~Button();
};
};

33
include/EzUI/CheckBox.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include "Label.h"
namespace ezui {
class UI_EXPORT CheckBox :
public Label
{
private:
bool m_checked = false;
public:
//选中样式
ControlStyle CheckedStyle;
//选中状态发送变化的回调函数
std::function<void(CheckBox* sender, bool checked)> CheckedChanged = NULL;
protected:
virtual ControlStyle& GetStyle(const ControlState& _state)override;
virtual void OnMouseDown(const MouseEventArgs& arg)override;
virtual void OnDpiChange(const DpiChangeEventArgs& args)override;
virtual void OnMouseEnter(const MouseEventArgs& args)override;
virtual void OnMouseUp(const MouseEventArgs& args)override;
virtual void OnMouseLeave(const MouseEventArgs& args)override;
public:
CheckBox(Object* parentObject = NULL);
virtual void SetAttribute(const UIString& key, const UIString& value)override;
//设置选中状态
virtual void SetCheck(bool checked);
//获取选中状态
virtual bool GetCheck();
virtual ~CheckBox();
};
};

51
include/EzUI/ComboBox.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#include "TextBox.h"
#include "Label.h"
#include "VListView.h"
#include "PopupWindow.h"
#include "HLayout.h"
namespace ezui {
//简易的下拉列表框
class UI_EXPORT ComboBox :public HLayout {
private:
//添加选项请使用AddItem
virtual Control* Add(Control* childCtl)override;
//移除选项请使用RemoveItem
virtual void Remove(Control* childCtl, bool freeCtrl)override;
private:
//下拉菜单选项
class MenuContent :public PopupWindow {
public:
Control* m_hittestCtl;
MenuContent(Control* ownerCtl, Control* hittestCtl);
virtual void OnKillFocus(HWND wnd) override;
virtual ~MenuContent();
};
private:
//下拉菜单窗口
MenuContent* m_menuWnd = NULL;
//选择之后显示的文本框
TextBox m_textBox;
//展开菜单的按钮
Label m_UpDown;
VListView m_list;
int m_index = -1;
void Init();
protected:
virtual void OnLayout()override;
public:
ComboBox(Object* parentObject = NULL);
//获取选中的文字
UIString GetText();
//获取选中的下标
int GetCheck();
//选中某个下标
bool SetCheck(int pos);
virtual ~ComboBox();
//添加一个item并返回新item的下标
int AddItem(const UIString& text);
void RemoveItem(int index);
};
};

527
include/EzUI/Control.h Normal file
View File

@@ -0,0 +1,527 @@
#pragma once
#include "EzUI.h"
namespace ezui {
class UI_EXPORT Control :public Object
{
private:
//顶层窗口句柄
HWND m_hWnd = NULL;
// 控件是否已经被移除或释放
bool* m_bRemove = NULL;
//控件是否被为按住状态
bool m_pressed = false;
// 控件是否启用,禁止状态下鼠标键盘消息将不可用
bool m_enabled = true;
// 控件是否可见。此标志为 true 时,控件为显示状态
bool m_bVisible = true;
// 当前控件的 DPI 缩放比例
float m_scale = 1.0f;
// 子控件集合
Controls m_controls;
// 管理图片的释放
PtrManager<Image*> m_imgs;
// 布局状态
// AddControl、InsertControl、RemoveControl、OnSize 时此标志为挂起状态
// 调用 ResumeLayout 标志为布局中
// 调用 OnLayout() 之后标志为 None
ezui::LayoutState m_layoutState = ezui::LayoutState::None;
// 鼠标悬浮提示文字
UIString m_tipsText;
// 上一次位置
Point m_lastLocation;
// 上一次大小
Size m_lastSize;
// 是否根据内容自动宽度
bool m_bAutoWidth = false;
// 根据内容自动高度变化
bool m_bAutoHeight = false;
// 控件内容宽高
Size m_contentSize;
// 绝对尺寸
Size m_fixedSize;
//比例尺寸(优先级最高)
SizeF m_rateSize;
// 控件矩形区域(基于父控件)
Rect m_rect;
// 控件在窗口中的可见区域
Rect m_viewRect;
// dock 样式
DockStyle m_dock = DockStyle::None;
// 控件是否可以被命中(值为false情况下就是穿透效果)
bool m_hitTestEnabled = true;
protected:
// 基于控件中的可见控件集合
Controls ViewControls;
// 控件当前状态
ControlState State = ControlState::Static;
public:
// 外边距
// 让容器独占一行或一列时,设置边距会使控件变小,不可设置为负数
Distance Margin;
// 控件的 ObjectName ID
UIString Name;
// 控件行为
ControlAction Action = ControlAction::None;
// 静态默认样式
ControlStyle Style;
//禁用状态样式
ControlStyle DisabledStyle;
// 鼠标悬浮样式
ControlStyle HoverStyle;
// 鼠标按下样式
ControlStyle ActiveStyle;
// 父控件指针
Control* Parent = NULL;
//是否添加到所在窗口/IFrame中的OnNotify函数中
Event NotifyFlags = Event::OnMouseEvent | Event::OnKeyBoardEvent;
// 事件处理器
std::function<void(Control*, EventArgs&)> EventHandler = NULL;
private:
// 禁止拷贝构造
Control(const Control&) = delete;
// 禁止赋值
Control& operator=(const Control&) = delete;
// 计算基于父控件的裁剪区域
void ComputeClipRect();
// 所有事件优先进入此函数(内部处理)
void OnEvent(EventArgs& arg);
protected:
//属性或者css样式都适用(css样式和属性都可以设置这些,只对静态样式生效)
virtual bool ApplyStyleProperty(const UIString& key, const UIString& value);
// 设置内容宽度,仅限子类使用
virtual void SetContentWidth(int width);
// 设置内容高度,仅限子类使用
virtual void SetContentHeight(int height);
// 设置内容尺寸,仅限子类使用
virtual void SetContentSize(const Size& size);
// 绘制之前
virtual void OnPaintBefore(PaintEventArgs& args);
// 控件绘制
virtual void OnPaint(PaintEventArgs& args);
// 子控件绘制,可重载此函数优化鼠标操作性能
virtual void OnChildPaint(PaintEventArgs& args);
// 背景绘制
virtual void OnBackgroundPaint(PaintEventArgs& painter);
// 前景绘制
virtual void OnForePaint(PaintEventArgs& e);
// 边框绘制
virtual void OnBorderPaint(PaintEventArgs& painter, const Border& border);
// 坐标发生改变
virtual void OnMove(const MoveEventArgs& arg);
// 大小发生改变
virtual void OnSize(const SizeEventArgs& arg);
// DPI 发生改变
virtual void OnDpiChange(const DpiChangeEventArgs& arg);
// 控件布局逻辑,需重写布局请重写此函数
virtual void OnLayout();
// 鼠标在控件上移动
virtual void OnMouseMove(const MouseEventArgs& arg);
// 鼠标离开控件
virtual void OnMouseLeave(const MouseEventArgs& args);
// 鼠标滚轮事件
virtual void OnMouseWheel(const MouseEventArgs& arg);
// 鼠标按下事件
virtual void OnMouseDown(const MouseEventArgs& arg);
// 鼠标弹起事件
virtual void OnMouseUp(const MouseEventArgs& arg);
// 鼠标双击事件
virtual void OnMouseDoubleClick(const MouseEventArgs& arg);
// 鼠标移入控件
virtual void OnMouseEnter(const MouseEventArgs& arg);
// 鼠标事件统一入口
virtual void OnMouseEvent(const MouseEventArgs& args);
// 键盘事件统一入口
virtual void OnKeyBoardEvent(const KeyboardEventArgs& _args);
// 字符输入事件WM_CHAR
virtual void OnKeyChar(const KeyboardEventArgs& _args);
// 键盘按下事件WM_KEYDOWN
virtual void OnKeyDown(const KeyboardEventArgs& _args);
// 键盘弹起事件WM_KEYUP
virtual void OnKeyUp(const KeyboardEventArgs& _args);
// 获得焦点事件
virtual void OnFocus(const FocusEventArgs& _args);
// 失去焦点事件
virtual void OnKillFocus(const KillFocusEventArgs& _args);
// 被移除时执行的逻辑
virtual void OnRemove();
public:
// 获取当前控件状态下的样式信息
virtual ControlStyle& GetStyle(const ControlState& _state);
// 获取左上圆角半径
int GetBorderTopLeftRadius(ControlState _state = ControlState::None);
// 获取右上圆角半径
int GetBorderTopRightRadius(ControlState _state = ControlState::None);
// 获取右下圆角半径
int GetBorderBottomRightRadius(ControlState _state = ControlState::None);
// 获取左下圆角半径
int GetBorderBottomLeftRadius(ControlState _state = ControlState::None);
// 获取左边框宽度
int GetBorderLeft(ControlState _state = ControlState::None);
// 获取上边框宽度
int GetBorderTop(ControlState _state = ControlState::None);
// 获取右边框宽度
int GetBorderRight(ControlState _state = ControlState::None);
// 获取下边框宽度
int GetBorderBottom(ControlState _state = ControlState::None);
// 获取边框颜色
Color GetBorderColor(ControlState _state = ControlState::None);
//获取边框样式
StrokeStyle GetBorderStyle(ControlState _state = ControlState::None);
// 获取前景图片
Image* GetForeImage(ControlState _state = ControlState::None);
// 获取背景图片
Image* GetBackImage(ControlState _state = ControlState::None);
// 获取背景颜色
Color GetBackColor(ControlState _state = ControlState::None);
// 获取旋转角度
float GetAngle(ControlState _state = ControlState::None);
//获取当前控件的鼠标光标
virtual HCURSOR GetCursor(ControlState _state = ControlState::None);
// 获取前景颜色
Color GetForeColor(ControlState _state = ControlState::None);
// 获取字体 Family
std::wstring GetFontFamily(ControlState _state = ControlState::None);
// 获取字体大小
int GetFontSize(ControlState _state = ControlState::None);
//获取公共数据
WindowData* GetPublicData();
//获取上层Frame容器
IFrame* GetFrame();
public:
// 构造函数 可传入父对象(由父对象自动管理内存)
Control(Object* parentObject = NULL);
// 析构函数
virtual ~Control();
//绑定对象(跟随释放)
using Object::Attach;
//分离对象(解除跟随释放)
using Object::Detach;
//绑定图片(跟随释放)
Image* Attach(Image* img);
//分离图片(解除跟随释放)
void Detach(Image* img);
//窗口句柄
HWND Hwnd();
//设置窗口句柄
void SetHwnd(HWND hWnd);
// 以下函数请保证在父控件布局已完成的情况下使用,使用 ResumeLayout() 执行布局
// 获取 X 坐标
int X();
// 获取 Y 坐标
int Y();
// 获取宽度
int Width();
// 获取高度
int Height();
// 设置 X 坐标
void SetX(int X);
// 设置 Y 坐标
void SetY(int Y);
// 移动相对于父控件的位置
void SetLocation(const Point& pt);
// 设置控件大小(当重绘控件时不建议多次使用,影响性能,会调用 SetRect 函数)
void SetSize(const Size& size);
// 设置绝对宽高
void SetFixedSize(const Size& size);
// 设置宽度(当重绘控件时不建议多次使用,影响性能,会调用 SetRect 函数)
void SetWidth(int width);
// 设置高度(当重绘控件时不建议多次使用,影响性能,会调用 SetRect 函数)
void SetHeight(int height);
// 设置绝对宽度
void SetFixedWidth(int fixedWidth);
// 设置绝对高度
void SetFixedHeight(int fixedHeight);
//设置宽度比例(优先级最高)
void SetRateWidth(float rateWidth);
//设置高度比例(优先级最高)
void SetRateHeight(float rateHeight);
// 设置相对父控件矩形,返回实际的 rect
const Rect& SetRect(const Rect& rect);
// 获取绝对宽度
int GetFixedWidth();
// 获取绝对高度
int GetFixedHeight();
// 获取光标位置
virtual Rect GetCareRect();
// 是否自动宽度
virtual bool IsAutoWidth();
// 是否自动高度
virtual bool IsAutoHeight();
// 设置自动宽度
virtual void SetAutoWidth(bool flag);
// 设置自动高度
virtual void SetAutoHeight(bool flag);
// 设置自动大小
virtual void SetAutoSize(bool flag);
// 获取控件内容大小
virtual const Size& GetContentSize();
// 获取控件大小
Size GetSize();
// 获取控件位置
Point GetLocation();
// 获取相对于父控件的矩形(布局计算后)
virtual const Rect& GetRect();
// 获取基于客户端区域的矩形
Rect GetClientRect();
//获取控件基于屏幕的矩形位置
Rect GetScreenRect();
// 获取控件的 DockStyle停靠方式
DockStyle GetDockStyle();
// 设置控件的 DockStyle
void SetDockStyle(const DockStyle& dockStyle);
// 获取控件的缩放系数
float GetScale();
// 是否存在挂起的布局
bool IsPendLayout();
// 尝试挂起布局,返回当前布局状态
const LayoutState TryPendLayout();
// 获取当前布局状态
const LayoutState GetLayoutState();
// 结束当前布局(使其立即生效)
void EndLayout();
// 立即强制刷新布局
virtual void RefreshLayout();
// 设置提示文字(类似 tooltip
void SetTips(const UIString& text);
// 获取提示文字
const UIString& GetTips();
// 获取控件的滚动条对象
virtual ScrollBar* GetScrollBar();
// 派发事件(如鼠标单击事件等...返回true则事件成功派发 返回false代表派发途中当前控件已被释放
void SendEvent(const EventArgs& arg);
// 设置控件属性
virtual void SetAttribute(const UIString& attrName, const UIString& attrValue);
// 获取当前可见的子控件集合
const Controls& GetViewControls();
// 获取所有子控件(不建议直接修改)
const Controls& GetControls();
// 使用下标获取控件,自动跳过 spacer 类控件
Control* GetControl(int pos);
// 是否包含指定控件(递归遍历所有子控件)
bool Contains(Control* ctl);
// 获取指定子控件的索引
int IndexOf(Control* childCtl);
// 根据 name 查找控件(包括自身)
Control* FindControl(const UIString& ctlName);
// 根据属性查找所有匹配控件(包括自身)
Controls FindControl(const UIString& attrName, const UIString& attrValue);
// 根据属性查找第一个匹配控件(包括自身)
Control* FindSingleControl(const UIString& attrName, const UIString& attrValue);
// 根据 name 查找子控件(仅限直接子集)
Control* FindChild(const UIString& ctlName);
// 根据属性查找所有匹配的子控件(仅限直接子集)
Controls FindChild(const UIString& attrName, const UIString& attrValue);
// 根据属性查找第一个匹配的子控件(仅限直接子集)
Control* FindSingleChild(const UIString& attrName, const UIString& attrValue);
// 交换两个子控件的位置
virtual bool SwapChild(Control* childCtl, Control* childCt2);
//是否启用控件
void SetEnabled(bool flag);
//是否禁用控件
void SetDisabled(bool flag);
//控件是否已启用
bool IsEnabled();
// 在指定位置插入子控件
virtual void Insert(int pos, Control* childCtl);
// 添加控件到末尾(如果是弹簧控件,在释放时将自动销毁)
virtual Control* Add(Control* childCtl);
// 移除控件freeCtrl 标志是否释放控件内存
virtual void Remove(Control* childCtl, bool freeCtrl = false);
// 设置控件的父控件
virtual void SetParent(Control* parentCtl);
// 清空所有子控件
virtual void Clear();
// 清空所有子控件freeChilds 决定是否释放子控件内存
virtual void Clear(bool freeChilds);
// 设置控件可见性
virtual void SetVisible(bool flag);
// 获取控件可见性状态
virtual bool IsVisible();
//设置控件是否可以被鼠标命中(false则是鼠标穿透效果)
void SetHitTestVisible(bool bEnable);
//控件是否可以被命中
bool IsHitTestVisible();
//隐藏控件
void Hide();
//显示控件
void Show();
// 标记控件区域为无效将会延迟刷新UI
virtual bool Invalidate();
// 立即强制刷新控件区域并更新无效区域(且立即触发布局)
virtual void Refresh();
//传入Style的引用 处理key value
virtual void SetStyle(ControlStyle& style, const UIString& key, const UIString& value);
//传入状态并分析样式字符串
virtual void SetStyleSheet(ControlState state, const UIString& styleStr);
//控件是否被按住
bool IsPressed();
};
};

View File

@@ -0,0 +1,268 @@
#pragma once
#include "UIDef.h"
#if USED_DIRECT2D
#ifndef UI_EXPORT
#define UI_EXPORT
#endif
#include <d2d1.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <wincodec.h>
#include "RenderTypes.h"
namespace ezui {
UI_EXPORT void RenderInitialize();//全局初始化direct2d
UI_EXPORT void RenderUnInitialize();//释放direct2d
UI_EXPORT float GetMaxRadius(float width, float height, float _radius);//获取最大半径 用于自动适应border-radius属性
class UI_EXPORT Font {
private:
Font() = delete;
std::wstring m_fontFamily;
float m_fontSize = 0;
IDWriteTextFormat* m_value = NULL;
bool m_ref = false;
void Copy(const Font& _copy);
public:
Font(const Font& _copy);
Font(const std::wstring& fontFamily, float fontSize);
float GetFontSize()const;
const std::wstring& GetFontFamily()const;
IDWriteTextFormat* Get() const;
bool operator==(const Font& _right);
virtual ~Font();
};
//文本命中测试数据
class UI_EXPORT HitTestMetrics {
public:
int Length;
int TextPos;//命中的下标
RectF FontBox;//文字的矩形位置
bool IsTrailingHit;//命中位置是否在尾部
public:
Rect GetCare() {
float x = FontBox.X;
if (IsTrailingHit) {
x += FontBox.Width;
}
float y = FontBox.Y;
return Rect((int)x, (int)y, 1, (int)(FontBox.Height + 0.5));
}
int GetFontHeight() {
return int(FontBox.Height + 0.5);
}
};
class UI_EXPORT TextLayout {
private:
TextLayout(const TextLayout& rightValue) = delete;
IDWriteTextLayout* m_textLayout = NULL;
std::wstring m_fontFamily;
DWRITE_TEXT_METRICS m_textMetrics = {};
std::vector<RectF> m_lineRects;
int m_unicodeSize = 0;
float m_fontSize = 0;
public:
void GetMetrics();
TextLayout(const std::wstring& text, const Font& font, const SizeF& maxSize = SizeF{ EZUI_FLOAT_MAX,EZUI_FLOAT_MAX }, TextAlign textAlgin = TextAlign::TopLeft);
Point HitTestPoint(const Point& pt, int* outTextPos, BOOL* outIsTrailingHit, int* fontHeight);
void HitTestPoint(const Point& pt, HitTestMetrics* hitTestMetrics);//根据坐标执行命中测试
Point HitTestTextPosition(int textPos, BOOL isTrailingHit);//根据文字下标执行命中测试
const std::wstring& GetFontFamily();
float GetFontSize();
int Width();
int Height();
//获取文本整体的占用空间
Size GetFontBox();
//获取每行文字的矩形位置
const std::vector<RectF>& GetLineRects();
int GetFontHeight();//获取字体高度
int GetLineCount();//获取一共有多少行
IDWriteTextLayout* Get() const;
void SetTextAlign(TextAlign textAlign);
void SetUnderline(int pos = 0, int count = 0);
virtual ~TextLayout();
};
//几何图形基础类(支持自定义路径)
class UI_EXPORT Geometry {
protected:
ID2D1GeometrySink* m_pSink = NULL;
ID2D1Geometry* m_rgn = NULL;
Geometry(const Geometry& rightCopy) = delete;
public:
Geometry();
virtual ~Geometry();
void AddArc(const PointF& endPoint, float radius);
void AddAcr(const D2D1_ARC_SEGMENT& arc);
void AddLine(const PointF& endPoint);
void BeginFigure(const PointF& startPoint, D2D1_FIGURE_BEGIN figureBegin = D2D1_FIGURE_BEGIN_FILLED);
void CloseFigure(D2D1_FIGURE_END figureEnd = D2D1_FIGURE_END_CLOSED);
ID2D1Geometry* Get()const;
public:
/// <summary>
/// 将两个几何图形通过指定的合并模式Union、Intersect、Xor、Exclude合并到一个输出几何中。
/// </summary>
/// <param name="out">合并结果输出到该 Geometry。</param>
/// <param name="a">参与合并的第一个 Geometry。</param>
/// <param name="b">参与合并的第二个 Geometry。</param>
/// <param name="COMBINE_MODE">几何合并模式,取值如 D2D1_COMBINE_MODE_UNION、INTERSECT、XOR、EXCLUDE。</param>
static void Combine(Geometry& out, const Geometry& a, const Geometry& b, D2D1_COMBINE_MODE COMBINE_MODE);
/// <summary>
/// 合并两个区域,取它们的联合部分(即最大边界区域)。
/// </summary>
static void Union(Geometry& out, const Geometry& a, const Geometry& b) {
Combine(out, a, b, D2D1_COMBINE_MODE::D2D1_COMBINE_MODE_UNION);
}
/// <summary>
/// 获取两个区域的交集部分。
/// </summary>
static void Intersect(Geometry& out, const Geometry& a, const Geometry& b) {
Combine(out, a, b, D2D1_COMBINE_MODE::D2D1_COMBINE_MODE_INTERSECT);
}
/// <summary>
/// 合并两个区域,保留不重叠的部分(异或运算)。
/// </summary>
static void Xor(Geometry& out, const Geometry& a, const Geometry& b) {
Combine(out, a, b, D2D1_COMBINE_MODE::D2D1_COMBINE_MODE_XOR);
}
/// <summary>
/// 从第一个区域中排除第二个区域的部分(差集)。
/// </summary>
static void Exclude(Geometry& out, const Geometry& a, const Geometry& b) {
Combine(out, a, b, D2D1_COMBINE_MODE::D2D1_COMBINE_MODE_EXCLUDE);
}
};
//矩形(已经完成闭合)
class UI_EXPORT RectangleGeometry :public Geometry {
private:
void Create(float x, float y, float width, float height, float _radius);
public:
RectangleGeometry(float x, float y, float width, float height, float _radius = 0);
RectangleGeometry(const RectF& _rect, float radius = 0);
RectangleGeometry(const RectF& _rect, float topLeftRadius, float topRightRadius, float bottomRightRadius, float bottomLeftRadius);
virtual ~RectangleGeometry() {};
};
//扇形(已经完成闭合)
class UI_EXPORT PieGeometry :public Geometry {
public:
PieGeometry(const RectF& rectF, float startAngle, float endAngle);
virtual ~PieGeometry() {};
};
//圆形/椭圆(已经完成闭合)
class UI_EXPORT EllipseGeometry :public PieGeometry {
public:
EllipseGeometry(const RectF& rectF) :PieGeometry(rectF, 0, 360) {}
virtual ~EllipseGeometry() {};
};
class UI_EXPORT DXImage : public IImage {
struct GifFrame
{
IWICBitmap* wicBitmap; // 存一份完整帧
UINT delay;
};
protected:
std::vector<GifFrame> m_frames;
IWICBitmapDecoder* m_bitmapdecoder = NULL;
IWICBitmapFrameDecode* m_pframe = NULL;
IWICFormatConverter* m_fmtcovter = NULL;//从文件加载
IWICBitmap* m_bitMap = NULL;//从HBITMAP中加载
int m_width = 0;
int m_height = 0;
ID2D1Bitmap* m_d2dBitmap = NULL;
private:
void CreateFormStream(IStream* istram);
void CreateFromFile(const std::wstring& file);
void Init();
public:
bool Visible = true;
void DecodeOfRender(ID2D1RenderTarget* render);
//如果HBITMAP带有透明通道 确保传入的图像颜色值已经与 Alpha 通道预乘
DXImage(HBITMAP hBitmap);
DXImage(IStream* istram);
DXImage(const std::wstring& file);
//创建带预乘Alpha的BGRA图片
DXImage(int width, int height);
DXImage(const void* data, size_t count);
ID2D1Bitmap* Get();
IWICBitmap* GetIWICBitmap();
int Width();
int Height();
virtual WORD NextFrame()override;
DXImage* Clone();
virtual ~DXImage();
};
class Bezier {
public:
Point point1;
Point point2;
Point point3;
};
};
namespace ezui {
class UI_EXPORT DXRender {
private:
ID2D1DCRenderTarget* m_render = NULL;
ID2D1SolidColorBrush* m_brush = NULL;
Font* m_font = NULL;
ID2D1StrokeStyle* m_pStrokeStyle = NULL;
Point m_offset;
PointF m_rotatePoint;
float m_angle = 0;
private:
DXRender(const DXRender& rightValue) = delete;
public:
ID2D1SolidColorBrush* GetBrush();
ID2D1StrokeStyle* GetStrokeStyle();
public:
DXRender(DXImage* dxImage);
DXRender(HDC dc, int x, int y, int width, int height);//创建dx绘图对象
virtual ~DXRender();
void SetFont(const std::wstring& fontFamily, float fontSize);//必须先调用
void SetFont(const Font& _copy_font);//必须先调用
void SetColor(const __EzUI__Color& color);//会之前必须调用
void SetStrokeStyle(StrokeStyle strokeStyle = StrokeStyle::Solid);//设置样式 虚线/实线
void DrawTextLayout(const TextLayout& textLayout, const PointF & = { 0,0 });//根据已有的布局绘制文字
void DrawString(const std::wstring& text, const RectF& _rect, ezui::TextAlign textAlign);//绘制文字
void DrawLine(const PointF& _A, const PointF& _B, float width = 1);//绘制一条线
void DrawRectangle(const RectF& _rect, float _radius = 0, float width = 1);//绘制矩形
void FillRectangle(const RectF& _rect, float _radius = 0);
//填充矩形
void PushLayer(const Geometry& dxGeometry);
void PopLayer();
void PushAxisAlignedClip(const RectF& rectBounds);
void PopAxisAlignedClip();
void SetTransform(float offsetX, float offsetY);//对画布进行旋转和偏移
void SetTransform(float startX, float startY, float angle);//设置旋转起始点与旋转角度
void SetTransform(float offsetX, float offsetY, float startX, float startY, float angle);
void DrawImage(DXImage* _image, const RectF& tagRect, float opacity = 1);//绘制图像
void DrawBezier(const PointF& startPoint, const Bezier& points, float width = 1);//贝塞尔线
void DrawBezier(const PointF& startPoint, std::list<Bezier>& points, float width = 1);//贝塞尔线
void DrawEllipse(const RectF& rectF, float width = 1);
void FillEllipse(const RectF& rectF);
void DrawPie(const RectF& rectF, float startAngle, float endAngle, float strokeWidth = 1);
void FillPie(const RectF& rectF, float startAngle, float endAngle);
void DrawPoint(const PointF& pt);
void DrawArc(const RectF& rect, float startAngle, float sweepAngle, float width = 1);//未实现
void DrawArc(const PointF& point1, const PointF& point2, const PointF& point3, float width = 1);
void DrawGeometry(ID2D1Geometry* path, float width = 1);
void FillGeometry(ID2D1Geometry* path);
void DrawGeometry(Geometry* path, float width = 1);
void FillGeometry(Geometry* path);
void Flush();
ID2D1DCRenderTarget* Get();//获取原生DX对象
};
};
#endif

530
include/EzUI/EzUI.h Normal file
View File

@@ -0,0 +1,530 @@
/*/
Author:yang
Email:19980103ly@gmail.com/718987717@qq.com
*/
#pragma once
#include "UIDef.h"
#include "UIString.h"
#include "Resource.h"
#include "RenderTypes.h"
#include "Direct2DRender.h"
#undef LoadCursor
#undef LoadIcon
namespace ezui {
struct MonitorInfo;
class Object;
class EventArgs;
class ControlStyle;
class IFrame;
class Control;
class Window;
class Spacer;
class ScrollBar;
class Bitmap;
enum class Cursor :ULONG_PTR;
#if 1
typedef std::vector<Control*> Controls;
#else
class UI_EXPORT Controls :public std::list<Control*> {
public:
//不要频繁使用此函数
inline Control* operator[](size_t right_pos)const
{
size_t pos = 0;
for (auto& it : *this) {
if (pos == right_pos) {
return it;
}
++pos;
}
return NULL;
}
};
#endif
//全局资源句柄
extern UI_VAR_EXPORT HMODULE __EzUI__HINSTANCE;//全局实例
extern UI_VAR_EXPORT Resource* __EzUI__Resource;//文件中的全局资源句柄
extern UI_VAR_EXPORT DWORD __EzUI__ThreadId;//UI的线程Id
extern UI_VAR_EXPORT HWND __EzUI_MessageWnd;//用于UI通讯的隐形窗口
extern UI_VAR_EXPORT const std::list<ezui::MonitorInfo> __EzUI__MonitorInfos;//所有监视器信息
//判断两个float是相等(两数是否接近)
extern UI_EXPORT bool IsFloatEqual(float num1, float num2);
//加载HICON
extern UI_EXPORT HICON LoadIcon(const UIString& fileName);
//装载字体
extern UI_EXPORT void InstallFont(const UIString& fontFileName);
//卸载字体
extern UI_EXPORT void UnstallFont(const UIString& fontFileName);
//复制内容到剪切板
extern UI_EXPORT bool CopyToClipboard(int uFormat, void* pData, size_t size, HWND hWnd = NULL);
//打开剪切板
extern UI_EXPORT bool GetClipboardData(int uFormat, std::function<void(void*, size_t)> Callback, HWND hWnd = NULL);
//复制unicode文字
extern UI_EXPORT bool CopyToClipboard(const std::wstring& str, HWND hWnd = NULL);
//粘贴unicode文字
extern UI_EXPORT bool GetClipboardData(std::wstring* outStr, HWND hWnd = NULL);
//自动获取文件资源(本地文件/资源文件)
extern UI_EXPORT bool GetResource(const UIString& fileName, std::string* outData);
//获取当前所有监视器的信息
extern UI_EXPORT size_t GetMonitor(std::list<MonitorInfo>* outMonitorInfo);
//获取用户当前所在的显示器
extern UI_EXPORT void GetMontior(MonitorInfo* outInfo, HWND hWnd = NULL);
//使用窗口的矩形位置获取所在的显示器
extern UI_EXPORT void GetMontior(MonitorInfo* outInfo, const Rect& rect);
//加载光标
extern UI_EXPORT HCURSOR LoadCursor(Cursor cursorType);
//加载光标(//需要释放)
extern UI_EXPORT HCURSOR LoadCursor(const UIString& fileName);
//释放光标
extern UI_EXPORT void FreeCursor(HCURSOR hCursor);
//默认处理OnNotify函数(处理一些控件的基础行为)
extern UI_EXPORT void DefaultNotify(Control* sender, EventArgs& args);
class UI_EXPORT Color :public ezui::__EzUI__Color {
public:
Color(const ezui::__EzUI__Color& copy) { this->BGRA = copy.GetValue(); }
Color(const DWORD& rgba = 0) :ezui::__EzUI__Color(rgba) {}
Color(BYTE r, BYTE g, BYTE b, BYTE a = 255) :ezui::__EzUI__Color(r, g, b, a) {}
public:
//构建一个Color
static Color Make(const UIString& colorStr, bool* isGood = NULL);
virtual ~Color() {}
};
#if USED_DIRECT2D
class UI_EXPORT Image :public DXImage {
private:
#ifdef _DEBUG
UIString m_path;
#endif
public:
virtual ~Image() {}
//创建带预乘Alpha的BGRA图片
Image(int width, int height) :DXImage(width, height) {}
Image(HBITMAP hBitmap) :DXImage(hBitmap) {}
Image(Bitmap* bitmap);
Image(IStream* iStream) :DXImage(iStream) {}
Image(const std::wstring& fileName) :DXImage(fileName) {}
Image(const void* data, size_t dataCount) :DXImage(data, dataCount) {}
public:
//从资源或者本地文件自动构建一个Image
static Image* Make(const UIString& fileOrRes);
};
#endif
// 定义用于保存显示器信息的结构体
struct MonitorInfo {
HMONITOR Monitor = NULL;
//显示器的位置 多显示器下Y轴可能出现负数或者大于0的时候代表显示器在设置里面显示器是错位的(多显示器没有平行);
//逻辑宽高
ezui::Rect Rect;
//工作区域
ezui::Rect WorkRect;
//显示器物理宽高
Size Physical;
//显示器缩放比例 1.0 1.25 1.5 1.75 2.0
float Scale = 1.0f;
//显示器帧率
float FPS = 60;
//是否为主显示器
bool Primary = false;
};
struct WindowData {
//缩放率
float Scale = 1.0f;
//单次绘图数量
int PaintCount = 0;
#ifdef _DEBUG
//是否开启debug模式
bool Debug = false;
//调试模式下的特有字段
int ColorIndex = 0;
Color DebugColor;
std::vector<Color> DebugColors{ Color::Red,Color::Green,Color::Blue,Color::Black,Color::White };
#endif
//主窗类的实例
ezui::Window* Window = NULL;
//使一个区域无效
std::function<void(const Rect&)> InvalidateRect = NULL;
//立即更新全部无效区域
std::function<void()> Refresh = NULL;
//清空控件标记等等...
std::function<void(Control*)> CleanControl = NULL;
//内部移动窗口的函数
std::function<void()> MoveWindow = NULL;
//内部使用标题部分移动窗口的函数
std::function<void()> TitleMoveWindow = NULL;
//处理消息过程的回调函数
std::function<LRESULT(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)> WndProc = NULL;
#ifdef _DEBUG
virtual ~WindowData() {
}
#endif
};
enum class LayoutState {
//无状态 (无需布局)
None,
//挂起中
Pend,
//布局中
Layouting
};
enum Event :long long {
None = 1,
OnMouseWheel = 2,
OnMouseEnter = 4,
OnMouseMove = 8,
OnMouseLeave = 16,
OnMouseDoubleClick = 32,
OnMouseDown = 64,
OnMouseUp = 128,
OnKeyDown = 256,
OnKeyUp = 512,
OnPaint = 1024,
OnFocus = 2048,
OnKillFocus = 4096,
OnKeyChar = 8192,
OnMove = 16384,
OnSize = 32768,
OnRect = 65536,
OnDpiChange = 131072,
OnActive = OnMouseDown | OnMouseUp,
OnHover = OnMouseEnter | OnMouseLeave,
OnMouseDrag = OnMouseDown | OnMouseMove,
OnMouseEvent = OnMouseWheel | OnMouseEnter | OnMouseMove | OnMouseLeave | OnMouseDoubleClick | OnMouseDown | OnMouseUp,
OnKeyBoardEvent = OnKeyDown | OnKeyUp | OnKeyChar
};
//重载枚举的 | 运算符
inline Event operator|(Event left, Event right)
{
return static_cast<Event>(static_cast<long long>(left) | static_cast<long long>(right));
}
//控件行为
enum class ControlAction {
None,
Title,//具有移动窗口 双击最大化窗口的行为
MoveWindow,//移动窗口
Mini,//最小化
Max,//最大化|恢复
Close//关闭
};
enum class ControlState :int {
None = 1,//无状态 则是使用_nowStyle缓存样式
Static = 2,//静态
Disabled = 4,//禁用状态
Checked = 8,//选中状态
Hover = 16,//鼠标悬浮
Active = 32//鼠标按住
};
EZUI_ENUM_OPERATORS(ControlState, int);
enum class DockStyle {
// 摘要:
//未设置
None,
// 摘要:
//在父控件中 左右保持
Horizontal,
// 摘要:
//在父控件中 上下保持
Vertical,
// 摘要:
// 铺满整个父控件
Fill
};
enum class MouseButton {
// 摘要:
// 未曾按下鼠标按钮。
None,
//
// 摘要:
// 鼠标左按钮曾按下。
Left,
//
// 摘要:
// 鼠标右按钮曾按下。
Right,
//
// 摘要:
// 鼠标中按钮曾按下。
Middle,
//
// 摘要:
// 第 1 个 XButton 曾按下。
XButton1,
//
// 摘要:
// 第 2 个 XButton 曾按下。
XButton2
};
enum class Cursor :ULONG_PTR
{
None = 0,//未指定
APPSTARTING = (ULONG_PTR)IDC_APPSTARTING,// 标准的箭头和小沙漏
ARROW = (ULONG_PTR)IDC_ARROW,// 标准的箭头
CROSS = (ULONG_PTR)IDC_CROSS,// 十字光标
HAND = (ULONG_PTR)IDC_HAND,// Windows 98/Me, Windows 2000/XP: Hand
HELP = (ULONG_PTR)IDC_HELP,// 标准的箭头和问号
IBEAM = (ULONG_PTR)IDC_IBEAM,// 工字光标
ICON = (ULONG_PTR)IDC_ICON,// Obsolete for applications marked version 4.0 or later.
NO = (ULONG_PTR)IDC_NO,// 禁止圈
SIZE = (ULONG_PTR)IDC_SIZE,// Obsolete for applications marked version 4.0 or later. Use SIZEALL.
SIZEALL = (ULONG_PTR)IDC_SIZEALL,// 四向箭头指向东、西、南、北
SIZENESW = (ULONG_PTR)IDC_SIZENESW,// 双箭头指向东北和西南
SIZENS = (ULONG_PTR)IDC_SIZENS, // 双箭头指向南北
SIZENWSE = (ULONG_PTR)IDC_SIZENWSE,// 双箭头指向西北和东南
SIZEWE = (ULONG_PTR)IDC_SIZEWE,// 双箭头指向东西
UPARROW = (ULONG_PTR)IDC_UPARROW,// 垂直箭头
WAIT = (ULONG_PTR)IDC_WAIT// 沙漏Windows7下会显示为选择的圆圈表示等待
};
//基础事件
class UI_EXPORT EventArgs {
public:
Event EventType = Event::None;
EventArgs(Event eventType) {
this->EventType = eventType;
}
virtual ~EventArgs() {};
};
//为鼠标事件提供基础数据
class UI_EXPORT MouseEventArgs :public EventArgs {
public:
MouseButton Button = MouseButton::None;
int ZDelta = 0;//方向
Point Location;
public:
MouseEventArgs(Event eventType, const Point& location = Point(0, 0), MouseButton mouseButton = MouseButton::None, int ZDelta = 0) :EventArgs(eventType) {
this->Button = mouseButton;
this->Location = location;
this->ZDelta = ZDelta;
}
virtual ~MouseEventArgs() {}
};
// 摘要:
//为键盘事件提供基础数据
class UI_EXPORT KeyboardEventArgs :public EventArgs {
public:
/// <summary>
/// 一般是指 键盘的ascii值
/// </summary>
WPARAM wParam;
LPARAM lParam;
KeyboardEventArgs(Event eventType, WPARAM wParam, LPARAM lParam) :EventArgs(eventType) {
this->wParam = wParam;
this->lParam = lParam;
}
virtual ~KeyboardEventArgs() {}
};
//获取焦点
class UI_EXPORT FocusEventArgs :public EventArgs {
public:
Control* Control;
FocusEventArgs(ezui::Control* ctl) :EventArgs(Event::OnFocus) {
this->Control = ctl;
}
virtual ~FocusEventArgs() {}
};
//失去焦点
class UI_EXPORT KillFocusEventArgs :public EventArgs {
public:
Control* Control;
KillFocusEventArgs(ezui::Control* ctl) :EventArgs(Event::OnKillFocus) {
this->Control = ctl;
}
virtual ~KillFocusEventArgs() {}
};
//坐标发生改变
class UI_EXPORT MoveEventArgs :public EventArgs {
public:
const ezui::Point Location;
MoveEventArgs(const ezui::Point& location) :EventArgs(Event::OnMove), Location(location) {}
virtual ~MoveEventArgs() {}
};
//大小发生改变
class UI_EXPORT SizeEventArgs :public EventArgs {
public:
const ezui::Size Size;
SizeEventArgs(const ezui::Size& size) :EventArgs(Event::OnSize), Size(size) {}
virtual ~SizeEventArgs() {}
};
//dpi发生变化
class UI_EXPORT DpiChangeEventArgs :public EventArgs {
public:
float Scale = 1.0f;
DpiChangeEventArgs(float scale) :EventArgs(Event::OnDpiChange), Scale(scale) {}
virtual ~DpiChangeEventArgs() {}
};
// 为 OnPaint 事件提供数据。
class UI_EXPORT PaintEventArgs :public EventArgs {
private:
std::list<bool> m_layers;
std::list<Point> m_offsets;
public:
PaintEventArgs(const PaintEventArgs&) = delete;
PaintEventArgs& operator=(const PaintEventArgs&) = delete;
WindowData* PublicData = NULL;
::HWND HWND = NULL;
HDC DC = NULL;
ezui::DXRender& Graphics;//画家
Rect InvalidRectangle;//WM_PAINT里面的无效区域
PaintEventArgs(ezui::DXRender& _painter) : EventArgs(Event::OnPaint), Graphics(_painter) {}
virtual ~PaintEventArgs() {}
//添加裁剪(速度较快)
void PushLayer(const Rect& rectBounds);
//添加异形裁剪 比较耗性能,但是可以异形抗锯齿裁剪
void PushLayer(const Geometry& dxGeometry);
//弹出最后一个裁剪
void PopLayer();
//放入一个偏移
void PushOffset(const Point& offset);
//弹出最后一个偏移
void PopOffset();
};
// 为控件样式提供数据。
class UI_EXPORT ControlStyle {
public:
//边框信息
ezui::Border Border;
//整体不透明度
//UI_Float Opacity;
//背景颜色
Color BackColor = 0;
//背景图片 如果指定的图片被删除 请必须将此置零
Image* BackImage = NULL;
//前景图片 如果指定的图片被删除 请必须将此置零
Image* ForeImage = NULL;
//字体名称 具有继承性
std::wstring FontFamily;
//字体大小 具有继承性
int FontSize = 0;
//前景颜色 具有继承性
Color ForeColor;
//鼠标样式
HCURSOR Cursor = NULL;
//旋转范围 0~360
float Angle = 0;
private:
void operator=(const ControlStyle& right) {} //禁止直接赋值 因为这样会导致 Color执行拷贝使得Color变得不合法的有效
ControlStyle(const ControlStyle& right) {} //禁止拷贝
public:
ControlStyle() {}
virtual ~ControlStyle() {}
void Scale(float scale);
};
//指针管理
template <typename T>
class PtrManager {
private:
std::vector<T> m_ptrs;
public:
PtrManager() {}
virtual ~PtrManager() {
for (auto& obj : m_ptrs) {
delete obj;
}
}
void Add(const T& v) {
if (v) {
m_ptrs.push_back(v);
}
}
void Remove(const T& v/*, bool bFree = false*/) {
auto it = std::find(m_ptrs.begin(), m_ptrs.end(), v);
if (it != m_ptrs.end()) {
/*if (bFree) {
delete(*it);
}*/
m_ptrs.erase(it);
}
}
void Clear() {
for (auto& obj : m_ptrs) {
delete obj;
}
m_ptrs.clear();
}
};
//常用对象基类
class UI_EXPORT Object {
private:
//属性集合
std::map<UIString, UIString> m_attrs;
// 管理子对象的释放
PtrManager<Object*> m_childObjects;
public:
//用户自定义数据
UINT_PTR Tag = NULL;
public:
Object(Object* parentObject = NULL);
virtual ~Object();
public:
//设置属性
virtual void SetAttribute(const UIString& attrName, const UIString& attrValue);
//获取属性
virtual UIString GetAttribute(const UIString& attrName);
//获取全部属性
virtual const std::map<UIString, UIString>& GetAttributes();
//移除某个属性
virtual void RemoveAttribute(const UIString& attrName);
//绑定对象(跟随释放)
virtual Object* Attach(Object* obj);
//分离对象(解除跟随释放)
virtual void Detach(Object* obj);
//延迟删除
void DeleteLater();
};
//原理采用PostMessage
template<class Func, class... Args>
bool BeginInvoke(Func&& f, Args&& ...args) {
HWND hWnd = ezui::__EzUI_MessageWnd;
if (hWnd == NULL || !::IsWindow(hWnd)) {
return false;
}
std::function<void()>* func = new std::function<void()>(std::bind(std::forward<Func>(f), std::forward<Args>(args)...));
if (::PostMessage(hWnd, WM_GUI_SYSTEM, WM_GUI_BEGININVOKE, (LPARAM)func) == LRESULT(0)) {
delete func;
return false;
}
return true;
}
//原理采用SendMessage
template<class Func, class... Args>
bool Invoke(Func&& f, Args&& ...args) {
std::function<void()> func(std::bind(std::forward<Func>(f), std::forward<Args>(args)...));
if (::GetCurrentThreadId() == ezui::__EzUI__ThreadId) {
func();
return true;
}
HWND hWnd = ezui::__EzUI_MessageWnd;
if (hWnd == NULL || !::IsWindow(hWnd)) {
return false;
}
if (::SendMessage(hWnd, WM_GUI_SYSTEM, WM_GUI_INVOKE, (LPARAM)&func) == LRESULT(-1)) {
return false;
}
return true;
}
//统计函数耗时
template<class Func, class... Args>
int64_t StopWatch(Func&& f, Args&& ...args) {
auto beginTime = std::chrono::steady_clock::now();
std::forward<Func>(f)(std::forward<Args>(args)...);
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - beginTime).count();
return delta;
}
};

18
include/EzUI/HLayout.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include "Control.h"
namespace ezui {
class UI_EXPORT HLayout :
public Control
{
public:
VAlign ContentAlign = VAlign::Mid;
protected:
virtual void OnLayout()override;
public:
HLayout(Object* parentObject = NULL);
virtual void SetAttribute(const UIString& key, const UIString& value)override;
virtual ~HLayout();
};
using HBox = HLayout;
};

21
include/EzUI/HListView.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "PagedListView.h"
#include "HScrollBar.h"
namespace ezui {
class UI_EXPORT HListView :
public PagedListView
{
private:
HScrollBar m_hScrollBar;
void Init();
void Offset(int offset);
protected:
virtual void OnLayout()override;
virtual void OnChildPaint(PaintEventArgs& args)override;
public:
HListView(Object* parentObject = NULL);
virtual ~HListView();
virtual ScrollBar* GetScrollBar()override;
};
};

21
include/EzUI/HScrollBar.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "Control.h"
#include "ScrollBar.h"
namespace ezui {
class UI_EXPORT HScrollBar :
public ScrollBar
{
protected:
virtual void OnMouseDown(const MouseEventArgs& arg)override;
virtual void OnMouseMove(const MouseEventArgs& arg)override;
virtual void GetInfo(int* viewLength, int* contentLength, int* scrollBarLength)override;
public:
HScrollBar(Object* parentObject = NULL);
virtual ~HScrollBar();
virtual void ScrollTo(Control* ctl)override;
virtual void ParentSize(const Size& parentSize)override;
virtual Rect GetSliderRect()override;
};
};

32
include/EzUI/IFrame.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
#include "EzUI.h"
#include "UIManager.h"
namespace ezui {
//内联页面 内部控件与外部隔离
class UI_EXPORT IFrame :public Control {
private:
UIManager m_umg;//内部UI管理器
private:
virtual Control* Add(Control* childCtl)override;
virtual void Remove(Control* childCtl, bool freeCtrl = false)override;
public:
//对外暴露消息通知回调
std::function<void(Control*, EventArgs&)> NotifyHandler = NULL;
IFrame(Object* parentObject = NULL);
virtual ~IFrame();
//从文件中加载xml
void LoadXml(const UIString& fileName);
//从内存中加载xml
void LoadXml(const char* fileData, size_t fileSize);
//设置唯一布局
void SetLayout(Control* ctrl);
//获取布局
Control* GetLayout();
virtual void SetAttribute(const UIString& attrName, const UIString& attrValue)override;
//消息通知
virtual void OnNotify(Control* sender, EventArgs& args);
//获取UI管理器
UIManager* GetUIManager();
};
};

36
include/EzUI/Label.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include "Control.h"
namespace ezui {
class UI_EXPORT Label :
public Control
{
private:
std::wstring m_wstr;
int m_underlinePos = 0;//显示下划线起始下标
int m_underlineCount = 0;//显示下划线文字个数
std::wstring m_ellipsisText;//文字溢出将显示的文字
protected:
virtual void OnForePaint(PaintEventArgs& args) override;
virtual void OnDpiChange(const DpiChangeEventArgs& args)override;
virtual void OnLayout()override;
public:
//基于控件的文字的边距
ezui::Distance TextMargin;
//文字对齐方式
TextAlign TextAlign = TextAlign::MiddleCenter;
public:
Label(Object* parentObject = NULL);
virtual ~Label();
virtual void SetAttribute(const UIString& key, const UIString& value)override;
virtual void RefreshLayout() override;
//设置文字
void SetText(const UIString& text);
//获取文字
UIString GetText()const;
//设置文字溢出控件之后的显示文字
void SetElidedText(const UIString& text);
//设置下划线位置
void SetUnderline(int pos, int count);
};
};

View File

@@ -0,0 +1,30 @@
#pragma once
#include "BorderlessWindow.h"
#include "Bitmap.h"
#include "Task.h"
#include "Timer.h"
namespace ezui {
/// <summary>
/// //LayeredWindow //无边框 带阴影 窗口透明异形 窗口大小发生改变重绘
/// </summary>
class UI_EXPORT LayeredWindow :public BorderlessWindow
{
private:
std::list<Rect> m_invalidateRect;
Bitmap* m_winBitmap = NULL;
void UpdateLayeredWindow(HDC hdc);
void BeginPaint(Rect* rect);
void EndPaint();
void Paint();
protected:
virtual void OnSize(const Size& sz)override;
void InvalidateRect(const Rect& rect);
virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)override;
public:
//窗口透明度
float Opacity = 1.0f;
LayeredWindow(int width, int height, HWND owner = NULL, DWORD dwStyle = NULL, DWORD dwExStyle = NULL);
virtual ~LayeredWindow();
};
};

20
include/EzUI/Menu.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "EzUI.h"
namespace ezui {
//菜单类
class UI_EXPORT Menu :public Object
{
private:
HMENU m_hMenu = NULL;
public:
//菜单子项被选点击的回调事件 UINT:子项ID
std::function<void(UINT_PTR)> MouseClick = NULL;
Menu(Object* parentObj = NULL);
virtual ~Menu();
HMENU HMenu();
UINT_PTR Append(const UIString& text);
void Remove(const UINT_PTR id);
};
};

27
include/EzUI/NotifyIcon.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include "Menu.h"
#include <shellapi.h>
namespace ezui {
//系统托盘类
class UI_EXPORT NotifyIcon :public Object
{
private:
HWND m_hWnd = NULL;
Menu* m_menu = NULL;
NOTIFYICONDATAW m_nid = {};
WindowData m_publicData;
protected:
virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
//事件处理 只会返回鼠标事件
std::function<void(const MouseEventArgs&)> EventHandler = NULL;
NotifyIcon(Object* parentObj = NULL);
void SetIcon(HICON icon);
//设置鼠标悬停时显示的提示文本
void SetTips(const UIString& text);
void SetMenu(Menu* menu);
void ShowBalloonTip(const UIString& title, const UIString& msg, int timeOut = 1000);
virtual ~NotifyIcon();
};
};

View File

@@ -0,0 +1,39 @@
#pragma once
#include "Control.h"
namespace ezui {
/**
* 是一个分页显示控件集合的容器控件
*
* 它支持对一批子控件进行分页管理,例如:
* - 显示每页固定数量的控件;
* - 支持翻页;
* - 当控件到达末页时需要手动调用NextPage进行加载下一页;
*
* 子类:VList/HList/TileList 继承使其具备分页管理的能力
*/
class UI_EXPORT PagedListView :
public Control
{
private:
int m_pageIndex = 0;
int m_pageTotal = 0;
int m_pageSize = 0;
Controls m_items;
public:
PagedListView(Object* parentObject = NULL);
virtual ~PagedListView();
//页面需要加载下一页的时候发生
std::function<bool(PagedListView*, int)> NextPaging = NULL;
void SetPageInfo(const Controls& items, int pageSize);
/// <summary>
/// 获取某页的item集合
/// </summary>
/// <param name="index">1~N</param>
/// <param name="outCtls">输出集合</param>
void GetPage(int index, Controls* outCtls);
virtual void NextPage();
virtual void Clear() override;
virtual void Clear(bool freeChilds) override;
};
};

20
include/EzUI/PictureBox.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "Control.h"
#include "Timer.h"
namespace ezui {
class UI_EXPORT PictureBox : public Control {
private:
Timer* m_timer;
private:
void Init();
protected:
virtual void OnForePaint(PaintEventArgs& arg)override;
public:
//图片(支持gif图自动播放)
Image* Image = NULL;
PictureBox(Object* parentObject = NULL);
virtual ~PictureBox();
virtual void SetAttribute(const UIString& key, const UIString& value)override;
};
};

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Window.h"
#include "BorderlessWindow.h"
#include "LayeredWindow.h"
namespace ezui {
/// <summary>
/// 弹出式窗口(失去焦点窗口将会关闭) 一般用于做右键菜单等等
/// </summary>
class UI_EXPORT PopupWindow :public LayeredWindow {
private:
Control* m_ownerCtl = NULL;
protected:
virtual void OnKillFocus(HWND hWnd) override;
public:
PopupWindow(int width, int height, HWND ownerHwnd);
PopupWindow(int width, int height, Control* ownerCtl = NULL);
virtual void Show()override;
virtual int ShowModal(bool disableOnwer = false)override;
virtual ~PopupWindow();
};
};

View File

@@ -0,0 +1,76 @@
#pragma once
#include "Control.h"
namespace ezui {
/// <summary>
/// 进度条控件
/// </summary>
class UI_EXPORT ProgressBar : public Control {
private:
float m_progress = 0.0f; // 进度值 0.0f - 1.0f
Color m_progressColor = Color::Blue; // 进度条颜色
Color m_backgroundColor = Color::LightGray; // 背景颜色
public:
ProgressBar(Object* parentObject = NULL);
virtual ~ProgressBar();
/// <summary>
/// 设置进度值
/// </summary>
/// <param name="progress">进度值,范围 0.0f - 1.0f</param>
void SetProgress(float progress);
/// <summary>
/// 获取当前进度值
/// </summary>
/// <returns>当前进度值</returns>
float GetProgress() const;
/// <summary>
/// 设置进度条颜色
/// </summary>
/// <param name="color">进度条颜色</param>
void SetProgressColor(const Color& color);
/// <summary>
/// 获取进度条颜色
/// </summary>
/// <returns>进度条颜色</returns>
Color GetProgressColor() const;
/// <summary>
/// 设置背景颜色
/// </summary>
/// <param name="color">背景颜色</param>
void SetBackgroundColor(const Color& color);
/// <summary>
/// 获取背景颜色
/// </summary>
/// <returns>背景颜色</returns>
Color GetBackgroundColor() const;
/// <summary>
/// 设置控件属性
/// </summary>
/// <param name="attrName">属性名</param>
/// <param name="attrValue">属性值</param>
virtual void SetAttribute(const UIString& attrName, const UIString& attrValue) override;
protected:
/// <summary>
/// 绘制控件
/// </summary>
/// <param name="args">绘制参数</param>
virtual void OnPaint(PaintEventArgs& args) override;
/// <summary>
/// 应用样式属性
/// </summary>
/// <param name="key">属性键</param>
/// <param name="value">属性值</param>
/// <returns>是否成功应用</returns>
virtual bool ApplyStyleProperty(const UIString& key, const UIString& value) override;
};
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "CheckBox.h"
namespace ezui {
class UI_EXPORT RadioButton :
public CheckBox
{
protected:
virtual void OnMouseDown(const MouseEventArgs& arg)override;
public:
RadioButton(Object* parentObject = NULL);
virtual~RadioButton();
};
};

813
include/EzUI/RenderTypes.h Normal file
View File

@@ -0,0 +1,813 @@
#pragma once
#include <Windows.h>
namespace ezui {
//size模式
enum class SizeMode {
//图片强行拉伸完全填充控件
//不裁剪,图片变形
Stretch,
//图片缩放后完全填充控件
//图片会裁剪, 保持比例裁剪和控件同大小
Cover,
//图片缩放后完整居中显示在控件上
//控件会留白
Fit,
//图片保持原尺寸,
//如果图片小于控件: 控件留白,
//如果图片大于控件: 控件边界外的部分被裁剪
Original
};
typedef SizeMode ImageSizeMode;
#if 1
#define Align_Top 1
#define Align_Bottom 2
#define Align_Left 4
#define Align_Right 8
#define Align_Mid 16
#define Align_Center 32
#else //GDI
#define Align_Top DT_TOP
#define Align_Bottom DT_BOTTOM
#define Align_Left DT_LEFT
#define Align_Right DT_RIGHT
#define Align_Mid DT_VCENTER
#define Align_Center DT_CENTER
#endif
/// <summary>
/// 水平状态下的对齐方式
/// </summary>
enum class HAlign :int
{
Left = Align_Left,
Center = Align_Center,
Right = Align_Right
};
EZUI_ENUM_OPERATORS(HAlign, int);
/// <summary>
/// 垂直状态下的对齐方式
/// </summary>
enum class VAlign :int
{
Top = Align_Top,
Mid = Align_Mid,
Bottom = Align_Bottom
};
EZUI_ENUM_OPERATORS(VAlign, int);
//包含垂直与水平对齐方式
enum class Align :int {
//
// 摘要:
// 内容在垂直方向上顶部对齐,在水平方向上左边对齐。
TopLeft = (int)VAlign::Top | (int)HAlign::Left,
//
// 摘要:
// 内容在垂直方向上顶部对齐,在水平方向上居中对齐。
TopCenter = (int)VAlign::Top | (int)HAlign::Center,
//
// 摘要:
// 内容在垂直方向上顶部对齐,在水平方向上右边对齐。
TopRight = (int)VAlign::Top | (int)HAlign::Right,
//
// 摘要:
// 内容在垂直方向上中间对齐,在水平方向上左边对齐。
MiddleLeft = (int)VAlign::Mid | (int)HAlign::Left,
//
// 摘要:
// 内容在垂直方向上中间对齐,在水平方向上居中对齐。
MiddleCenter = (int)VAlign::Mid | (int)HAlign::Center,
//
// 摘要:
// 内容在垂直方向上中间对齐,在水平方向上右边对齐。
MiddleRight = (int)VAlign::Mid | (int)HAlign::Right,
//
// 摘要:
// 内容在垂直方向上底边对齐,在水平方向上左边对齐。
BottomLeft = (int)VAlign::Bottom | (int)HAlign::Left,
//
// 摘要:
// 内容在垂直方向上底边对齐,在水平方向上居中对齐。
BottomCenter = (int)VAlign::Bottom | (int)HAlign::Center,
//
// 摘要:
// 内容在垂直方向上底边对齐,在水平方向上右边对齐。
BottomRight = (int)VAlign::Bottom | (int)HAlign::Right
};
EZUI_ENUM_OPERATORS(Align, int);
typedef Align TextAlign;
enum class FontStyle {
NORMAL
/* DWRITE_FONT_STYLE_NORMAL
字体样式 :正常。
DWRITE_FONT_STYLE_OBLIQUE
字体样式 :倾斜。
DWRITE_FONT_STYLE_ITALIC
字体样式 :斜体。 */
};
//描边样式
enum class StrokeStyle
{
None,//无
Solid,//实线
Dash//虚线
};
template<typename T>
class __EzUI__Size
{
public:
T Width;
T Height;
public:
__EzUI__Size()
{
Width = Height = 0;
}
__EzUI__Size(const __EzUI__Size& __Size)
{
Width = __Size.Width;
Height = __Size.Height;
}
__EzUI__Size(T width,
T height)
{
Width = width;
Height = height;
}
bool operator!=(const __EzUI__Size& _right) const
{
return !(Width == _right.Width && Height == _right.Height);
}
void Scale(float scale) {
Width = (Width * scale) + 0.5;
Height = (Height * scale) + 0.5;
}
bool Equals(const __EzUI__Size& sz) const
{
return (Width == sz.Width) && (Height == sz.Height);
}
bool Empty() const
{
return (Width == 0 && Height == 0);
}
__EzUI__Size operator+(const __EzUI__Size& sz) const
{
return __EzUI__Size(Width + sz.Width,
Height + sz.Height);
}
__EzUI__Size operator-(const __EzUI__Size& sz) const
{
return __EzUI__Size(Width - sz.Width,
Height - sz.Height);
}
bool operator==(const __EzUI__Size& _right) const
{
return Equals(_right);
}
};
template<typename T>
class __EzUI__Point
{
public:
T X;
T Y;
public:
__EzUI__Point()
{
X = Y = 0;
}
__EzUI__Point(const __EzUI__Point& __Point)
{
X = __Point.X;
Y = __Point.Y;
}
__EzUI__Point(const __EzUI__Size<T>& __Size)
{
X = __Size.Width;
Y = __Size.Height;
}
__EzUI__Point(T x,
T y)
{
X = x;
Y = y;
}
void Scale(float scale) {
X = (X * scale) + 0.5;
Y = (Y * scale) + 0.5;
}
bool Equals(const __EzUI__Point& __Point)const
{
return (X == __Point.X) && (Y == __Point.Y);
}
__EzUI__Point operator+(const __EzUI__Point& __Point) const
{
return __Point(X + __Point.X,
Y + __Point.Y);
}
__EzUI__Point operator-(const __EzUI__Point& __Point) const
{
return __Point(X - __Point.X,
Y - __Point.Y);
}
bool operator==(const __EzUI__Point& __Point) const
{
return Equals(__Point);
}
};
template<typename T>
class __EzUI__Rect
{
public:
T X;
T Y;
T Width;
T Height;
public:
__EzUI__Rect()
{
X = Y = Width = Height = 0;
}
__EzUI__Rect(T x,
T y,
T width,
T height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
__EzUI__Rect(const RECT& rect) {
X = rect.left;
Y = rect.top;
Width = rect.right - rect.left;
Height = rect.bottom - rect.top;
}
__EzUI__Rect(const __EzUI__Point<T>& location, const __EzUI__Size<T>& size) {
X = location.X;
Y = location.Y;
Width = size.Width;
Height = size.Height;
}
__EzUI__Point<T> GetLocation() const
{
return __EzUI__Point<T>{ X, Y };
}
__EzUI__Size<T> GetSize() const
{
return __EzUI__Size<T>(Width, Height);
}
RECT ToRECT() const {
return RECT{ (LONG)GetLeft(), (LONG)GetTop(), (LONG)GetRight(), (LONG)GetBottom() };
}
T GetLeft() const
{
return X;
}
T GetTop() const
{
return Y;
}
T GetRight() const
{
return X + Width;
}
T GetBottom() const
{
return Y + Height;
}
bool IsEmptyArea() const
{
return (Width <= 0) || (Height <= 0);
}
virtual const __EzUI__Rect& Scale(float scale) {
X = T((X * scale) + 0.5);
Y = T((Y * scale) + 0.5);
Width = T((Width * scale) + 0.5);
Height = T((Height * scale) + 0.5);
return *this;
}
bool Equals(const __EzUI__Rect& __Rect) const
{
return X == __Rect.X &&
Y == __Rect.Y &&
Width == __Rect.Width &&
Height == __Rect.Height;
}
bool operator == (const __EzUI__Rect& right) {
return Equals(right);
}
__EzUI__Rect& operator+=(T value) {
X -= value;
Y -= value;
Width += value * 2;
Height += value * 2;
return *this;
}
__EzUI__Rect& operator-=(T value) {
X += value;
Y += value;
Width -= value * 2;
Height -= value * 2;
return *this;
}
__EzUI__Rect operator+(T value) const {
__EzUI__Rect out = *this;
out += value;
return out;
}
__EzUI__Rect operator-(T value) const {
__EzUI__Rect out = *this;
out -= value;
return out;
}
bool Contains(T x,
T y) const
{
return x >= X && x < X + Width &&
y >= Y && y < Y + Height;
}
bool Contains(const __EzUI__Point<T>& pt) const
{
return Contains(pt.X, pt.Y);
}
bool Contains(const __EzUI__Rect& __Rect) const
{
return (X <= __Rect.X) && (__Rect.GetRight() <= GetRight()) &&
(Y <= __Rect.Y) && (__Rect.GetBottom() <= GetBottom());
}
void Inflate(T dx,
T dy)
{
X -= dx;
Y -= dy;
Width += 2 * dx;
Height += 2 * dy;
}
void Inflate(const __EzUI__Point<T>& point)
{
Inflate(point.X, point.Y);
}
bool Intersect(const __EzUI__Rect& __Rect)
{
return Intersect(*this, *this, __Rect);
}
static bool Intersect(__EzUI__Rect& c,
const __EzUI__Rect& a,
const __EzUI__Rect& b)
{
T right = min(a.GetRight(), b.GetRight());
T bottom = min(a.GetBottom(), b.GetBottom());
T left = max(a.GetLeft(), b.GetLeft());
T top = max(a.GetTop(), b.GetTop());
c.X = left;
c.Y = top;
c.Width = right - left;
c.Height = bottom - top;
return !c.IsEmptyArea();
}
bool IntersectsWith(const __EzUI__Rect& __Rect) const
{
return (GetLeft() < __Rect.GetRight() &&
GetTop() < __Rect.GetBottom() &&
GetRight() > __Rect.GetLeft() &&
GetBottom() > __Rect.GetTop());
}
static bool Union(__EzUI__Rect& c,
const __EzUI__Rect& a,
const __EzUI__Rect& b)
{
if (a.IsEmptyArea()) {
c = b;
return !c.IsEmptyArea();
}
if (b.IsEmptyArea()) {
c = a;
return !c.IsEmptyArea();
}
T right = max(a.GetRight(), b.GetRight());
T bottom = max(a.GetBottom(), b.GetBottom());
T left = min(a.GetLeft(), b.GetLeft());
T top = min(a.GetTop(), b.GetTop());
c.X = left;
c.Y = top;
c.Width = right - left;
c.Height = bottom - top;
return !c.IsEmptyArea();
}
void Offset(const __EzUI__Point<T>& point)
{
Offset(point.X, point.Y);
}
void Offset(T dx,
T dy)
{
X += dx;
Y += dy;
}
virtual ~__EzUI__Rect() {}
};
class __EzUI__Color
{
protected:
DWORD BGRA = 0;
public:
__EzUI__Color() {}
__EzUI__Color(
const BYTE& r,
const BYTE& g,
const BYTE& b,
const BYTE& a = 255)
{
((BYTE*)&BGRA)[0] = b;
((BYTE*)&BGRA)[1] = g;
((BYTE*)&BGRA)[2] = r;
((BYTE*)&BGRA)[3] = a;
}
__EzUI__Color(DWORD bgra)
{
BGRA = bgra;
}
virtual ~__EzUI__Color() {}
BYTE GetR() const
{
return ((BYTE*)&BGRA)[2];
}
BYTE GetG() const
{
return ((BYTE*)&BGRA)[1];
}
BYTE GetB() const
{
return ((BYTE*)&BGRA)[0];
}
BYTE GetA() const
{
return ((BYTE*)&BGRA)[3];
}
DWORD GetValue() const
{
return BGRA;
}
void SetValue(DWORD bgra)
{
BGRA = bgra;
}
void SetR(BYTE value) {
((BYTE*)&BGRA)[2] = value;
}
void SetG(BYTE value) {
((BYTE*)&BGRA)[1] = value;
}
void SetB(BYTE value) {
((BYTE*)&BGRA)[0] = value;
}
void SetA(BYTE value) {
((BYTE*)&BGRA)[3] = value;
}
public:
// Common color constants (BGRA format)
enum : DWORD
{
Transparent = 0x00000000, // 全透明
Black = 0xFF000000, // 黑色
White = 0xFFFFFFFF, // 白色
Gray = 0xFF808080, // 灰色
LightGray = 0xFFD3D3D3, // 浅灰色
DarkGray = 0xFFA9A9A9, // 深灰色
Red = 0xFFFF0000, // 红色
DarkRed = 0xFF8B0000, // 深红色
LightCoral = 0xFFF08080, // 浅珊瑚红
Tomato = 0xFFFF6347, // 番茄色
Crimson = 0xFFDC143C, // 猩红色
Green = 0xFF00FF00, // 绿色
Lime = 0xFF00FF00, // 酸橙绿(亮绿)
DarkGreen = 0xFF006400, // 深绿色
LawnGreen = 0xFF7CFC00, // 草坪绿
PaleGreen = 0xFF98FB98, // 苍绿色
Blue = 0xFF0000FF, // 蓝色
RoyalBlue = 0xFF4169E1, // 皇家蓝
DodgerBlue = 0xFF1E90FF, // 道奇蓝
DeepSkyBlue = 0xFF00BFFF, // 深天蓝
LightBlue = 0xFFADD8E6, // 浅蓝色
Yellow = 0xFF00FFFF, // 黄色
Gold = 0xFFFFD700, // 金色
LightYellow = 0xFFFFFFE0, // 浅黄色
Khaki = 0xFFF0E68C, // 卡其色
Orange = 0xFFFFA500, // 橙色
DarkOrange = 0xFFFF8C00, // 深橙色
Coral = 0xFFFF7F50, // 珊瑚色
Salmon = 0xFFFA8072, // 鲑鱼色
Purple = 0xFF800080, // 紫色
MediumPurple = 0xFF9370DB,// 中紫色
Indigo = 0xFF4B0082, // 靛青色
Violet = 0xFFEE82EE, // 紫罗兰
Plum = 0xFFDDA0DD, // 李子紫
Cyan = 0xFF00FFFF, // 青色
Teal = 0xFF808000, // 水鸭色G+B
Aqua = 0xFF00FFFF, // 浅绿色(水色)
Turquoise = 0xFF40E0D0, // 绿松石色
Brown = 0xFFA52A2A, // 棕色
Maroon = 0xFF800000, // 栗色(褐红)
Tan = 0xFFD2B48C, // 茶色
Beige = 0xFFF5F5DC, // 米色
Navy = 0xFF000080, // 藏青色
Olive = 0xFF808000, // 橄榄色
Silver = 0xFFC0C0C0 // 银色
};
};
template<typename T>
class __EzUI__Line {
public:
__EzUI__Point<T> pointA;
__EzUI__Point<T> pointB;
public:
__EzUI__Line() {
pointA.X = 0;
pointA.Y = 0;
pointB.X = 0;
pointB.Y = 0;
}
__EzUI__Line(const __EzUI__Point<T>& _pointA, const __EzUI__Point<T>& _pointB) {
this->pointA = _pointA;
this->pointB = _pointB;
}
};
typedef __EzUI__Point<int> Point;
typedef __EzUI__Point<float> PointF;
typedef __EzUI__Line<int> Line;
typedef __EzUI__Line<float> LineF;
typedef __EzUI__Size<int> Size;
typedef __EzUI__Rect<int> Rect;
class SizeF :public __EzUI__Size<float> {
public:
SizeF()
{
Width = Height = 0;
}
SizeF(float width,
float height)
{
Width = width;
Height = height;
}
SizeF(const SizeF& __Size)
{
Width = __Size.Width;
Height = __Size.Height;
}
SizeF(const Size& __Size)
{
Width = (float)__Size.Width;
Height = (float)__Size.Height;
}
};
class RectF :public __EzUI__Rect<float> {
public:
RectF() {
this->X = 0;
this->Y = 0;
this->Width = 0;
this->Height = 0;
}
RectF(const Rect& rect) {
this->X = (float)rect.X;
this->Y = (float)rect.Y;
this->Width = (float)rect.Width;
this->Height = (float)rect.Height;
}
RectF(const RectF& rect) {
this->X = rect.X;
this->Y = rect.Y;
this->Width = rect.Width;
this->Height = rect.Height;
}
RectF(float x, float y, float width, float height) {
this->X = x;
this->Y = y;
this->Width = width;
this->Height = height;
}
virtual const RectF& Scale(float scale) {
X = (X * scale);
Y = (Y * scale);
Width = (Width * scale);
Height = (Height * scale);
return *this;
}
//转换
static RectF Transformation(SizeMode sizeMode, const RectF& container, const SizeF& contentSize) {
if (sizeMode == SizeMode::Stretch) {
return container;
}
//容器数据
float containerWidth = container.Width;
float containerHeight = container.Height;
float containerRatio = containerWidth / containerHeight;//宽高比
//内容数据
float contentWidth = contentSize.Width;
float contentHeight = contentSize.Height;
float contentRatio = contentWidth / contentHeight; //宽高比
if (sizeMode == SizeMode::Fit) {
if (containerRatio < contentRatio) {
float zoomHeight = containerWidth / contentWidth * contentHeight;
float y = (containerHeight - zoomHeight) / 2.0f + container.Y;
return RectF(container.X, y, containerWidth, zoomHeight);
}
else {
float zoomWidth = containerHeight / contentHeight * contentWidth;
float x = (containerWidth - zoomWidth) / 2.0f + container.X;
return RectF(x, container.Y, zoomWidth, containerHeight);
}
}
if (sizeMode == SizeMode::Cover) {
if (containerRatio < contentRatio) {
//1000 670 容器大小
//1000 300 内容大小
//2233 670 缩放后的内容大小
float zoomWidth = containerHeight / contentHeight * contentWidth;//内容应该这么宽才对
float x = (zoomWidth - containerWidth) / 2.0f;
return RectF(container.X - x, container.Y, zoomWidth, containerHeight);
}
else {
//1000 600 容器大小
//400 600 内容大小
//1000 1500 缩放后的内容大小
float zoomHeight = containerWidth / contentWidth * contentHeight;//内容应该这么高才对
float y = (zoomHeight - containerHeight) / 2.0f;
return RectF(container.X, container.Y - y, containerWidth, zoomHeight);
}
}
//按照内容原大小居中显示
if (sizeMode == SizeMode::Original) {
float x = (container.Width - contentSize.Width) / 2.0f;
float y = (container.Height - contentSize.Height) / 2.0f;
return RectF(x, y, contentSize.Width, contentSize.Height);
}
return container;
}
virtual ~RectF() {};
};
struct Distance {
public:
WORD Left, Top, Right, Bottom;
Distance() {
Left = Top = Right = Bottom = 0;
}
Distance(WORD distanceAll) {
Left = Top = Right = Bottom = distanceAll;
}
Distance& operator=(WORD distanceAll) {
Left = Top = Right = Bottom = distanceAll;
return *this;
}
void Scale(float scale) {
Top = WORD(Top * scale + 0.5);
Bottom = WORD(Bottom * scale + 0.5);
Left = WORD(Left * scale + 0.5);
Right = WORD(Right * scale + 0.5);
}
//获取垂直所占空间
WORD GetVSpace() {
return Top + Bottom;
}
//获取水平所占空间
WORD GetHSpace() {
return Left + Right;
}
};
/// <summary>
/// 描述边框的一些信息
/// </summary>
class Border {
public:
WORD Left = 0;//左边边框大小
WORD Top = 0;//顶部边框大小
WORD Right = 0;//右边边框大小
WORD Bottom = 0;//底部边框大小
WORD TopLeftRadius = 0;
WORD TopRightRadius = 0;
WORD BottomRightRadius = 0;
WORD BottomLeftRadius = 0;
__EzUI__Color Color;
StrokeStyle Style = StrokeStyle::None;
public:
class Radius {
Border& Border;
public:
Radius(ezui::Border& bd) :Border(bd) {}
//对四个角度同时设置半径大小
Radius& operator=(WORD radius) {
Border.TopLeftRadius = radius;
Border.TopRightRadius = radius;
Border.BottomRightRadius = radius;
Border.BottomLeftRadius = radius;
return *this;
}
};
public:
Border::Radius Radius = (*this);
public:
Border() {}
//对四个边设置大小
Border& operator=(WORD borderWidth) {
Left = borderWidth;
Top = borderWidth;
Right = borderWidth;
Bottom = borderWidth;
return *this;
}
void Scale(float scale) {
Left = WORD(Left * scale + 0.5);
Top = WORD(Top * scale + 0.5);
Right = WORD(Right * scale + 0.5);
Bottom = WORD(Bottom * scale + 0.5);
TopLeftRadius = WORD(TopLeftRadius * scale + 0.5);
TopRightRadius = WORD(TopRightRadius * scale + 0.5);
BottomRightRadius = WORD(BottomRightRadius * scale + 0.5);
BottomLeftRadius = WORD(BottomLeftRadius * scale + 0.5);
}
};
class IImage {
protected:
WORD m_frameCount = 0;//总帧数
WORD m_framePos = 0;//当前帧率索引
public:
Rect Clip;//取出图像部分区域进行绘制
Point DrawPosition;//绘制在owner矩形坐标
ezui::Size DrawSize;//绘制在owner矩形的大小
ImageSizeMode SizeMode = ImageSizeMode::Fit;// 图像显示模式
public:
virtual ~IImage() {}
WORD FrameCount() {
return m_frameCount;
}
//跳转到下一帧 并且获取下一帧的延迟
virtual WORD NextFrame() = 0;
};
};

50
include/EzUI/Resource.h Normal file
View File

@@ -0,0 +1,50 @@
#pragma once
#include "UIString.h"
namespace ezui {
/// <summary>
/// 框架中的资源类
/// </summary>
class UI_EXPORT Resource {
public:
struct Entry {
std::streampos Offset = 0;//偏移
std::streamsize Size = 0;//大小
UIString Name;//名称
};
//资源文件读取流
class UI_EXPORT ReadStream {
std::streampos m_pos = 0;
std::streamsize m_count = 0;
const char* m_ptr = NULL;
std::ifstream* m_ifs = NULL;
public:
ReadStream(HRSRC hRsrc);
ReadStream(const UIString& fileName);
void seekg(std::streampos pos);
void read(char* buf, std::streamsize count);
std::streampos tellg();
const std::streamsize size();
virtual ~ReadStream();
};
private:
ReadStream* m_rStream = NULL;
void UnPackage();
bool m_isGood = false;
public:
const std::list<Entry> Items;
bool IsGood();
//对资源目录进行打包
static bool Package(const UIString& dir, const UIString& outFile, const std::function<void(const UIString&, int, int)>& packCallback = NULL);
public:
virtual ~Resource();
//从本地文件创建资源对象
Resource(const UIString& resFile);
//使用windows内置资源文件创建资源对象
Resource(HRSRC hRsrc);
//寻找资源中的文件
bool GetFile(const UIString& fileName, std::string* out);
//传入item直接返回数据
void GetFile(const Entry& item, std::string* out);
};
};

67
include/EzUI/ScrollBar.h Normal file
View File

@@ -0,0 +1,67 @@
#pragma once
#include "Control.h"
namespace ezui {
class UI_EXPORT ScrollBar :public Control {
protected:
//鼠标是否已经按下
bool m_mouseDown = false;
//上一次鼠标命中的坐标
int m_lastPoint = 0;
//滚动条当前的坐标
double m_sliderPos = 0;
//滚动条的长度
int m_sliderLength = 0;
//滚动条每滚动一次的比率
double m_rollRate = 0;
//父容器内的坐标偏移
int m_offset = 0;
//父容器的内容长度
int m_contentLength = 0;
//父容器可见长度(容器自身长度)
int m_viewLength = 0;
//溢出容器的长度
int m_overflowLength = 0;
//int _old_viewLength = 0;
//int _old_contentLength = 0;
//int _old_offset = 0;
private:
void Init();
public:
//滚动条计算出偏移之后的回调函数
std::function<void(int)> OffsetCallback = NULL;
//滚动事件 arg1:发送者 arg2:滚动百分比 arg3:滚动类型
std::function<void(ScrollBar*, float, Event)> Scroll = NULL;
protected:
virtual void OnBackgroundPaint(PaintEventArgs& arg)override;
virtual void OnForePaint(PaintEventArgs& args) override;
virtual void OnMouseDown(const MouseEventArgs& arg)override;
virtual void OnMouseUp(const MouseEventArgs& arg)override;
virtual void OnMouseLeave(const MouseEventArgs& arg) override;
virtual void OnMouseWheel(const MouseEventArgs& arg)override;
virtual void GetInfo(int* viewLength, int* contentLength, int* scrollBarLength) = 0;
void ScrollTo(int offset, const Event& type);
void SyncInfo();
public:
//滚动到指定控件可见位置
virtual void ScrollTo(Control* ctl) = 0;
//按照百分比滚动 0.0f~1.0f
void ScrollTo(float scrollRate);
//获取当前滚动到的位置 进度的百分比
float ScrollPos();
//获取滑块的矩形
virtual Rect GetSliderRect() = 0;//
virtual void ParentSize(const Size& parentSize) = 0;
//滚动条是否已经绘制且显示
bool IsDraw();
//重置滚动条数据到起点(不执行重绘)
void Reset();
//滚动条是否能够滚动
bool Scrollable();
//当父控件发生内容发生改变 请调用刷新滚动条
void RefreshScroll();
ScrollBar(Object* parentObject = NULL);
virtual ~ScrollBar();
};
};

28
include/EzUI/ShadowBox.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include "Window.h"
#include "Bitmap.h"
namespace ezui {
class UI_EXPORT ShadowBox
{
private:
Size m_lastSize;
int m_lastShadowMargin = 0;
Bitmap* m_bufBitmap = NULL;
HWND m_hWnd = NULL;
HWND m_mainHWnd = NULL;
WORD m_radius = 0;
WindowData* m_publicData = NULL;
private:
void SetAplpha(int x, int y, BYTE a, float radius);
bool SetShadow(int m_Width, int m_Height, int iSize, float radius);
protected:
virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
ShadowBox(int width, int height, HWND mainHwnd);//构造函数
virtual ~ShadowBox();
//在父窗口发生改变的时候更新阴影区域
virtual void Update(int shadowMargin, int radius);
HWND Hwnd();
};
};

23
include/EzUI/Spacer.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include "Control.h"
namespace ezui {
//添加弹簧无需用户手动释放(不可在栈上创建弹簧对象)
class UI_EXPORT Spacer :public Control {
public:
Spacer();
virtual ~Spacer();
};
//具有绝对高度的 的弹簧
class UI_EXPORT VSpacer :public Spacer {
public:
virtual ~VSpacer();
VSpacer(int fixedHeight = 0);
};
//具有绝对宽度的 的弹簧
class UI_EXPORT HSpacer :public Spacer {
public:
virtual ~HSpacer();
HSpacer(int fixedWidth = 0);
};
};

43
include/EzUI/TabLayout.h Normal file
View File

@@ -0,0 +1,43 @@
#pragma once
#include "Control.h"
#include "Timer.h"
namespace ezui {
//滑动方向
enum class SlideDirection {
Horizontal, // 横向滑动(比如从左滑到右)
Vertical // 纵向滑动(比如从上滑到底)
};
class UI_EXPORT TabLayout :
public Control
{
private:
int m_pageIndex = 0;
Timer* m_timer;
int m_offset = 0;
int m_nowOffset = 0;
std::vector<int> m_initial;
SlideDirection m_dlideDirection;
float m_stepAcc = 0;
float m_stepPerFrame = 0;
void Sort();
void Init();
protected:
virtual void OnLayout()override;
virtual void SetAttribute(const UIString& key, const UIString& value)override;
virtual void OnChildPaint(PaintEventArgs& args)override;
public:
TabLayout(Object* parentObject = NULL);
virtual ~TabLayout();
virtual void Remove(Control* ctl, bool freeCtl = false)override;
virtual Control* Add(Control* childCtl)override;
//设置当前显示页
void SetPageIndex(int index);
//动画方式滑动到某一页
void SlideToPage(int index, SlideDirection dlideDirection = SlideDirection::Horizontal, int durationMs = 150, int fps = 90);
void SetPage(Control* ctl);
Control* GetPage();
//获取当前页索引
int GetPageIndex();
};
};

98
include/EzUI/Task.h Normal file
View File

@@ -0,0 +1,98 @@
#pragma once
#include "EzUI.h"
namespace ezui {
//互斥锁
class UI_EXPORT Mutex {
private:
CRITICAL_SECTION m_mtx; // 互斥锁
bool m_bLocked;
Mutex(const Mutex&) = delete;
public:
Mutex();
virtual ~Mutex();
// 锁定互斥锁
void Lock();
// 解锁互斥锁
void UnLock();
};
//带互斥锁的条件变量类
class UI_EXPORT ConditionVariable {
private:
HANDLE m_codv = NULL; // 事件对象(模拟条件变量)
Mutex m_mtx; // 锁对象
ConditionVariable(const ConditionVariable&) = delete;
public:
ConditionVariable();
virtual ~ConditionVariable();
// 唤醒等待的线程(唤醒单个线程)
void Notify();
// 可选条件的等待(不可以多个线程使用此函数)
void Wait(const std::function<bool()>& condition_cb = NULL);
// 锁定互斥锁
void Lock();
// 解锁互斥锁
void Unlock();
};
};
namespace ezui {
class UI_EXPORT Task {
bool m_finished = false;
std::thread* m_thread = NULL;
bool m_bJoin = false;
private:
Task(const Task&) = delete;
void DoWork(std::function<void()>* func);
public:
template<class Func, class... Args>
Task(Func&& f, Args&& ...args) {
std::function<void()>* func = new std::function<void()>(std::bind(std::forward<Func>(f), std::forward<Args>(args)...));
m_thread = new std::thread([this, func]() mutable {
DoWork(func);
});
}
void Wait();
//当前任务是否已经停止
bool IsStopped();
virtual ~Task();
};
class UI_EXPORT TaskFactory {
bool m_bStop = false;
std::list<Task*> m_tasks;
std::list<std::function<void()>> m_funcs;
std::mutex m_mtx;
std::condition_variable m_codv;
//用于等待任务清空的锁和条件变量
std::mutex m_mtx2;
std::condition_variable m_codv2;
private:
TaskFactory(const TaskFactory&) = delete;
public:
TaskFactory(int maxTaskCount = 50);
//添加到任务队列中的末尾(先后顺序执行)
template<class Func, class... Args>
void Add(Func&& f, Args&& ...args) {
{
std::unique_lock<std::mutex> autoLock(m_mtx);
m_funcs.emplace_back(std::bind(std::forward<Func>(f), std::forward<Args>(args)...));
}
m_codv.notify_one();
}
//添加至任务队列的第一位(优先执行)
template<class Func, class... Args>
void AddToFrist(Func&& f, Args&& ...args) {
{
std::unique_lock<std::mutex> autoLock(m_mtx);
m_funcs.emplace_front(std::bind(std::forward<Func>(f), std::forward<Args>(args)...));
}
m_codv.notify_one();
}
//等待所有任务被取走
void WaitAll();
virtual ~TaskFactory();
};
};

105
include/EzUI/TextBox.h Normal file
View File

@@ -0,0 +1,105 @@
#pragma once
#include "Control.h"
#include "VScrollBar.h"
#include "Timer.h"
namespace ezui {
class UI_EXPORT TextBox :
public Control
{
private:
VScrollBar m_vScrollbar;
int m_lastWidth = 0;
int m_lastHeight = 0;
bool m_multiLine = false;
std::wstring m_text;//文字
Size m_fontBox;
bool m_down = false;//是否具有焦点中
bool m_focus = false;//是否具有焦点中
Point m_point_Start;//开始选中的位置
Point m_point_End;//结束位置
std::vector<RectF> m_selectRects;//选中的字符矩形
Rect m_careRect;//光标位置
Font* m_font = NULL;//字体
TextLayout* m_textLayout = NULL;//字体布局
Point m_pointA;//A点
BOOL m_A_isTrailingHit;//如果是1表示是字符的后半边
int m_A_TextPos = 0;//点击了第几个字符
Point m_pointB;//B点
BOOL m_B_isTrailingHit;//如果是1表示是字符的后半边
int m_B_TextPos = 0;//点击了第几个字符
int m_textPos = 0;//当前文字的下标 0~text.size()
int m_scrollX = 0;//用于左右滚动
int m_scrollY = 0;//用于y轴滚动
int m_lastX = 0;//上一个x位置
int m_lastY = 0;//上一个y位置
Timer* m_timer;//用于光标闪烁
bool m_bCareShow = false;//用于光标闪烁
int m_maxLen = -1;//最大文字数量
std::wstring m_placeholder;//placeholder懂得都懂 (在没有文字的情况下显示的文字)
std::wstring m_passwordChar;
bool m_readOnly = false;//是否只读
public:
//文字对其方式(针对单行输入框有效)
TextAlign TextAlign = TextAlign::MiddleLeft;
private:
void Init();
void InsertUnicode(const std::wstring& str);//插入unicode文字内部使用
bool DeleteRange();//删除选中内容
bool GetSelectedRange(int* outPos, int* outCount);//获取当前被选中的区域 返回下标和个数
bool Copy();//复制到剪切板
bool Paste();//粘贴
bool SelectedAll();//全选
void OnBackspace();//退格键要做的事
void BuildCare();//构建光标
void BuildSelectedRect();
Point ConvertPoint(const Point& pt);//坐标转换
protected:
virtual void OnRemove()override;
virtual void SetAutoWidth(bool flag)override;
virtual void SetAutoHeight(bool flag)override;
virtual void OnForePaint(PaintEventArgs& e) override;
virtual void OnKeyChar(const KeyboardEventArgs& arg) override;
virtual void OnKeyDown(const KeyboardEventArgs& arg)override;
virtual void OnMouseDown(const MouseEventArgs& arg)override;
virtual void OnMouseWheel(const MouseEventArgs& arg)override;
virtual void OnMouseMove(const MouseEventArgs& arg) override;
virtual void OnMouseUp(const MouseEventArgs& arg)override;
virtual void OnFocus(const FocusEventArgs& arg) override;
virtual void OnKillFocus(const KillFocusEventArgs& arg) override;
virtual void OnLayout();
void Offset(int moveY);
public:
std::function<void(const UIString&)> TextChanged = NULL;
public:
TextBox(Object* parentObject = NULL);
virtual ~TextBox();
virtual void SetAttribute(const UIString& key, const UIString& value)override;
//获取焦点所在光标位置
virtual Rect GetCareRect()override;
//分析字符串
void Analysis();
//在当前光标中插入文字
void Insert(const UIString& str);
//获取输入框文字
const UIString GetText();
//获取滚动条
virtual ScrollBar* GetScrollBar()override;
//设置文字
void SetText(const UIString& text);
//是否多行显示
bool IsMultiLine();
//设置是否多行显示
void SetMultiLine(bool multiLine);
//设置为是否只读
void SetReadOnly(bool bReadOnly);
//是否为只读
bool IsReadOnly();
//设置最大输入字符个数
void SetMaxLength(int maxLen);
// 设置占位符文本
void SetPlaceholderText(const UIString& text);
//设置密码框占位符(建议单字符)
void SetPasswordChar(const UIString& passwordChar);
};
};

View File

@@ -0,0 +1,22 @@
#pragma once
#include "PagedListView.h"
#include "VScrollBar.h"
namespace ezui {
class UI_EXPORT TileListView :
public PagedListView
{
private:
VScrollBar m_vScrollBar;
bool m_autoHeight = false;//根据内容的高度自动变化
void Init();
void Offset(int offset);
protected:
virtual void OnChildPaint(PaintEventArgs& args)override;
virtual void OnLayout()override;
public:
TileListView(Object* parentObject = NULL);
virtual ~TileListView();
virtual ScrollBar* GetScrollBar()override;
};
};

39
include/EzUI/Timer.h Normal file
View File

@@ -0,0 +1,39 @@
#pragma once
#include "EzUI.h"
#include "Task.h"
namespace ezui {
//使用线程的计时器 不与主进程同步(启动的时候就直接开始执行回调函数)
class UI_EXPORT Timer :public Object {
bool m_bExit = false;
std::atomic<bool> m_bPause = true;
Task* m_task = NULL;
HANDLE m_event = NULL;
public:
std::function<void(Timer*)> Tick = NULL;
int Interval = 0;
public:
//Timeout干啥的不用我多说什么了吧
template<class Func, class... Args>
static void Timeout(int msec, Func&& f, Args&& ...args) {
std::function<void()>* func = new std::function<void()>(std::bind(f, args...));
Timer* timer = new Timer;
timer->Interval = 0;
timer->Tick = [msec, func](Timer* t) {
t->Stop();
Sleep(msec);
(*func)();
delete func;
t->DeleteLater();
};
timer->Start();
};
public:
Timer(Object* parentObject = NULL);
bool IsStopped();
void Start();
void Stop();
virtual ~Timer();
};
};

14
include/EzUI/TreeView.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include "HListView.h"
#include "VListView.h"
namespace ezui {
//树形菜单 还在设计中
class UI_EXPORT TreeView :public HListView {
private:
VListView m_vList;
public:
TreeView(Object* parentObj = NULL);
void AddNode(const UIString& nodeName);
virtual ~TreeView();
};
};

116
include/EzUI/UIDef.h Normal file
View File

@@ -0,0 +1,116 @@
#pragma once
#include <list>
#include <vector>
#include <map>
#include <exception>
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <memory>
#include <functional>
#include <thread>
#include <chrono>
#include <mutex>
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <windows.h>
#ifndef GET_X_LPARAM
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#endif // !GET_X_LPARAM
#ifndef GET_Y_LPARAM
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
#endif // !GET_Y_LPARAM
#ifndef ASSERT
#ifdef _DEBUG
#define ASSERT(expr) _ASSERTE(expr)
#else
#define ASSERT(expr) ((void)0)
#endif
#endif
#ifndef GCL_HCURSOR
#define GCL_HCURSOR -12
#endif
#ifdef _WIN64
#define UI_SET_USERDATA(hWnd,data) SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)data);
#define UI_GET_USERDATA(hwnd) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
#else
#define UI_SET_USERDATA(hWnd,data) SetWindowLongW(hWnd, GWLP_USERDATA, (LONG)data);
#define UI_GET_USERDATA(hwnd) GetWindowLongW(hwnd, GWLP_USERDATA);
#endif
#if defined _M_IX86
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IA64
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
//框架内保留的消息 用于GUI框架内部通讯
#define WM_GUI_SYSTEM WM_USER
#define WM_GUI_APP WM_APP
//扩展消息 在WM_GUI_SYSTEM消息中的wParam参数中体现
#define WM_GUI_INVOKE 0x01
#define WM_GUI_BEGININVOKE 0x02
#if defined(EZUI_STATIC)
#define UI_EXPORT
#define UI_VAR_EXPORT
#elif defined(_WINDLL)
#define UI_EXPORT __declspec(dllexport)
#define UI_VAR_EXPORT __declspec(dllexport)
#else
#define UI_EXPORT
#define UI_VAR_EXPORT __declspec(dllimport)
#endif // EZUI_STATIC
#define EZUI_WINDOW_CLASS L"EzUI_Window" //基础窗口类名
#define EZUI_INVOKER_WINDOW_CLASS L"EzUI_InvokerWindow" //用于线程同步的窗口类名
#define EZUI_FLOAT_MAX 16777216.0f //最后一个可以精确表示的整数
#define EZUI_FLOAT_EPSILON 1e-6f // 浮点误差阈值
//下面的渲染方式只能选一个
#define USED_DIRECT2D 1 //DX绘制 性能好 内存占用高
//生成枚举类常用操作符
#define EZUI_ENUM_OPERATORS(ENUM_TYPE, BASE_TYPE) \
inline ENUM_TYPE operator|(ENUM_TYPE a, ENUM_TYPE b) { \
return static_cast<ENUM_TYPE>(static_cast<BASE_TYPE>(a) | static_cast<BASE_TYPE>(b)); \
} \
inline ENUM_TYPE operator&(ENUM_TYPE a, ENUM_TYPE b) { \
return static_cast<ENUM_TYPE>(static_cast<BASE_TYPE>(a) & static_cast<BASE_TYPE>(b)); \
} \
inline ENUM_TYPE operator~(ENUM_TYPE a) { \
return static_cast<ENUM_TYPE>(~static_cast<BASE_TYPE>(a)); \
} \
inline ENUM_TYPE operator^(ENUM_TYPE a, ENUM_TYPE b) { \
return static_cast<ENUM_TYPE>(static_cast<BASE_TYPE>(a) ^ static_cast<BASE_TYPE>(b)); \
} \
inline ENUM_TYPE& operator|=(ENUM_TYPE& a, ENUM_TYPE b) { \
a = a | b; \
return a; \
} \
inline ENUM_TYPE& operator&=(ENUM_TYPE& a, ENUM_TYPE b) { \
a = a & b; \
return a; \
} \
inline ENUM_TYPE& operator^=(ENUM_TYPE& a, ENUM_TYPE b) { \
a = a ^ b; \
return a; \
}

77
include/EzUI/UIManager.h Normal file
View File

@@ -0,0 +1,77 @@
#pragma once
#include "Control.h"
#include "Spacer.h"
#include "HLayout.h"
#include "Label.h"
#include "VLayout.h"
#include "TileListView.h"
#include "Button.h"
#include "VListView.h"
#include "HListView.h"
#include "RadioButton.h"
#include "CheckBox.h"
#include "TextBox.h"
#include "TabLayout.h"
#include "PictureBox.h"
#include "ProgressBar.h"
#include "Window.h"
#include "ComboBox.h"
namespace ezui {
//主窗口中的内联页面类
class UI_EXPORT UIManager {
public:
struct Style
{
ControlState m_styleType;
UIString m_selectorName;
UIString m_styleStr;
};
struct XmlNode {
Control* m_ctl;
UIString m_tagName;
public:
XmlNode(Control* ctl, const UIString& tagName) :m_ctl(ctl), m_tagName(tagName) {}
};
private:
std::vector<Control*> m_rootNode;//根节点列表
std::list<XmlNode> m_controls;
std::list<UIManager::Style> m_styles;
void LoadControl(void* node, Control* control);
Control* BuildControl(void* node);//内部函数
//记录XML中的控件到管理器 管理器释放的时候 由管理器加载的控件将自动释放
void RegisterControl(Control* ctl, const UIString& tagNamee);
void AnalysisStyle(const UIString& styleStr, std::list<UIManager::Style>* out);//分析样式
void ApplyStyle(Control* ctl, const std::list<UIManager::Style>& selectors, const UIString& tagName);
//应用样式(为控件应用所有样式)
protected:
//当解析到一个节点的时候发生
virtual Control* OnBuildControl(const UIString& nodeName);
//获取根节点控件
Control* GetRoot(int index = 0);
public:
UIManager();
virtual ~UIManager();
void SetupUI(Window* window);
void SetupUI(Control* parentCtl);
//从文件中加载布局(不允许多次加载xml)
void LoadXml(const UIString& fileName);
//从内存加载布局(不允许多次加载xml)
void LoadXml(const char* fileData, size_t fileSize);
//设置样式表
void SetStyleSheet(const UIString& styleContent);
//从文件中加载样式
void LoadStyle(const UIString& fileName);
//释放由本此对象创建的控件
void Free(Control** ctl);
};
//注册基础控件
void InitControls();
//注册自定义控件
void RegisterControl(const UIString& ctrlName, const std::function<Control* ()>& create_cb);
//注册自定义控件
template<typename T>
void RegisterControl(const UIString& ctrlName) {
RegisterControl(ctrlName, []() -> Control* { return new T; });
}
};

28
include/EzUI/UISelector.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include "UIManager.h"
namespace ezui {
//控件选择器(多功能选择器暂时未完善)
class UI_EXPORT UISelector
{
private:
Control* m_ctl = NULL;
Control* m_notCtl = NULL;
std::vector<Control*> m_ctls;
UISelector& NextName(const UIString& key) { return *this; };
UISelector& NextId(const UIString& key) { return *this; };
public:
UISelector(const std::vector<Control*>& controls);
UISelector(const std::list<Control*>& controls);
UISelector(Control* control);
UISelector(Control* control, const UIString& mathStr);
virtual ~UISelector();
UISelector& Css(const UIString& styleStr);
UISelector& CssHover(const UIString& styleStr);
UISelector& CssActive(const UIString& styleStr);
UISelector& Attr(const UIString& key, const UIString& value);
UISelector& Refresh();
UISelector& Not(Control* fiterCtl);
};
#define $ UISelector
};

78
include/EzUI/UIString.h Normal file
View File

@@ -0,0 +1,78 @@
#pragma once
#include "UIDef.h"
#include <codecvt>
#include <iomanip>
namespace ezui {
namespace ui_text {
//-----------------------------------------------Copy Start-----------------------------------------------
/// <summary>
/// utf8字符串
/// </summary>
class UI_EXPORT String :public std::string {
public:
String();
virtual ~String();
String(const String& _right)noexcept;
String(String&& _right)noexcept;
String& operator=(const String& _right)noexcept;
String& operator=(String&& _right)noexcept;
String(const std::string& str)noexcept;
String(const char* szbuf)noexcept;
String(const wchar_t* szbuf)noexcept;
String(const std::wstring& wstr)noexcept;
//返回utf8字符个数
size_t utf8Length() const;
std::wstring unicode() const;
std::string ansi() const;
void erase(char _ch);
void erase(size_t pos, size_t count);
String replace(char oldChar, char newChar)const;
String replace(const String& oldText, const String& newText, bool allReplace = true)const;
String toLower()const;
String toUpper()const;
//去除前后空格
String trim()const;
//find value count
size_t count(const String& value)const;
std::vector<String> split(const String& ch)const;
bool operator==(const wchar_t* szbuf)const;
bool operator==(const std::wstring& wStr)const;
template<typename ...T>
inline String format(const T &...args) {
auto bufSize = ::snprintf(NULL, 0, this->c_str(), std::forward<const T&>(args)...) + 1; // +1是为了'结束符\0'
char* buf = new char[bufSize] {0};
auto count = ::sprintf_s(buf, bufSize, this->c_str(), std::forward<const T&>(args)...);
String ret(buf);
delete[] buf;
return ret;
}
};
//base convert
UI_EXPORT void AnyToUnicode(const std::string& src_str, UINT codePage, std::wstring* out_wstr);
UI_EXPORT void UnicodeToAny(const std::wstring& unicode_wstr, UINT codePage, std::string* out_str);
//
UI_EXPORT void GBKToUTF8(const std::string& str, std::string* outStr);
UI_EXPORT void UTF8ToGBK(const std::string& str, std::string* outStr);
UI_EXPORT void ANSIToUniCode(const std::string& str, std::wstring* outStr);
UI_EXPORT void ANSIToUTF8(const std::string& str, std::string* outStr);
UI_EXPORT void UnicodeToANSI(const std::wstring& wstr, std::string* outStr);
UI_EXPORT void UnicodeToUTF8(const std::wstring& wstr, std::string* outStr);
UI_EXPORT void UTF8ToANSI(const std::string& str, std::string* outStr);
UI_EXPORT void UTF8ToUnicode(const std::string& str, std::wstring* outStr);
//
UI_EXPORT void Tolower(std::string* str_in_out);
UI_EXPORT void Toupper(std::string* str_in_out);
UI_EXPORT void Erase(std::string* str_in_out, char ch);
UI_EXPORT void Replace(std::string* str_in_out, char oldChar, char newChar);
UI_EXPORT size_t Replace(std::string* str_in_out, const std::string& oldText, const std::string& newText, bool replaceAll = true);
UI_EXPORT void Split(const std::string& str_in, const std::string& ch, std::vector<std::string>* strs_out);
//
UI_EXPORT String ToString(double number, size_t keepBitSize);
//-----------------------------------------------Copy End-----------------------------------------------
};
using UIString = ui_text::String;
};

18
include/EzUI/VLayout.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include "Control.h"
namespace ezui {
class UI_EXPORT VLayout :
public Control
{
public:
HAlign ContentAlign = HAlign::Center;
protected:
virtual void OnLayout() override;
public:
VLayout(Object* parentObject = NULL);
virtual void SetAttribute(const UIString& key, const UIString& value)override;
virtual ~VLayout();
};
using VBox = VLayout;
};

22
include/EzUI/VListView.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "PagedListView.h"
#include "VScrollBar.h"
namespace ezui {
class UI_EXPORT VListView :
public PagedListView
{
private:
VScrollBar m_vScrollBar;
void Init();
//对控件进行偏移
void Offset(int offset);
protected:
virtual void OnLayout()override;
virtual void OnChildPaint(PaintEventArgs& args)override;
public:
VListView(Object* parentObject = NULL);
virtual ~VListView();
virtual ScrollBar* GetScrollBar() override;
};
};

22
include/EzUI/VScrollBar.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "Control.h"
#include "ScrollBar.h"
namespace ezui {
class UI_EXPORT VScrollBar :
public ScrollBar
{
protected:
virtual void OnMouseDown(const MouseEventArgs& arg)override;
virtual void OnMouseMove(const MouseEventArgs& arg)override;
virtual void GetInfo(int* viewLength, int* contentLength, int* scrollBarLength)override;
public:
VScrollBar(Object* parentObject = NULL);
virtual ~VScrollBar();
virtual void ScrollTo(Control* ctl)override;
virtual void ParentSize(const Size& size)override;
virtual Rect GetSliderRect()override;
};
};

267
include/EzUI/Window.h Normal file
View File

@@ -0,0 +1,267 @@
#pragma once
#include "Control.h"
#include "ScrollBar.h"
#include "Spacer.h"
#undef IsMinimized
#undef IsMaximized
#undef IsRestored
namespace ezui {
/// <summary>
/// Window //经典带边框带系统菜单WIN32窗口样式
/// </summary>
class UI_EXPORT Window :public Object
{
private:
//具有鼠标焦点的控件
Control* m_focusControl = NULL;
//具有键盘焦点的控件
Control* m_inputControl = NULL;
//窗口公共数据
WindowData* m_publicData = NULL;
//窗口句柄
HWND m_hWnd = NULL;
//鼠标跟踪
bool m_bTracking = false;
//鼠标是否在里面
bool m_mouseIn = false;
//鼠标是否已经按下
bool m_mouseDown = false;
//窗口移动
bool m_moveWindow = false;
//记录鼠标坐标
POINT m_dragPoint;
//记录鼠标按下的坐标
Point m_downPoint;
//上一次鼠标按下的时间
ULONGLONG m_lastDownTime = 0;
//上一次鼠标按下的按钮
MouseButton m_lastBtn = MouseButton::None;
//窗口最小尺寸
Size m_miniSize;
//窗口最大尺寸
Size m_maxSize;
//当窗口关闭的时候退出代码
int m_closeCode = 0;
//基于桌面的坐标
Rect m_rect;
//客户绘图区域
Rect m_rectClient;
//所属窗口句柄
HWND m_ownerWnd = NULL;
//窗口根Frame
IFrame* m_frame = NULL;
// 管理图片的释放
PtrManager<Image*> m_imgs;
public:
//对外暴露消息通知回调
std::function<void(Control*, EventArgs&)> NotifyHandler = NULL;
private:
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
bool IsInWindow(Control& pControl, Control& it);
void Init(int width, int height, HWND owner, DWORD dStyle, DWORD dwExStyle);//初始窗口
//仅移动窗口
void MoveWindow();
//鼠标按下以标题栏方式移动窗口
void TitleMoveWindow();
//在窗口中使用基于客户区的鼠标位置寻找可命中的控件
Control* HitTestControl(const Point& clientPoint, Point* outPoint);
//派发事件
void SendEvent(Control* ctrl, const EventArgs& args);
protected:
//当dpi发生更改时
virtual void OnDpiChange(float systemScale, const Rect& newRect);
//鼠标移动时发生
virtual void OnMouseMove(const Point& point);
//当鼠标悬停时发生
virtual void OnMouseHover(const Point& point);
//鼠标离开时发生
virtual void OnMouseLeave();
//鼠标滚动发生
virtual void OnMouseWheel(int zDelta, const Point& point);
//鼠标双击是发生
virtual void OnMouseDoubleClick(MouseButton mbtn, const Point& point);
//鼠标按下时发生
virtual void OnMouseDown(MouseButton mbtn, const Point& point);
//鼠标弹起时发生
virtual void OnMouseUp(MouseButton mbtn, const Point& point);
//生成渲染器 渲染参数
virtual void DoPaint(HDC winDC, const Rect& rePaint);
//渲染中
virtual void OnPaint(PaintEventArgs& arg);
//位置发生改变时发生
virtual void OnMove(const Point& point);
//大小发生改变时发生
virtual void OnSize(const Size& sz);
//当窗口收到WM_CLOSE消息时发生
virtual void OnClose(bool& bClose);
//当窗口销毁时发生
virtual void OnDestroy();
//字符消息
virtual void OnKeyChar(WPARAM wParam, LPARAM lParam);
//键盘按下
virtual void OnKeyDown(WPARAM wParam, LPARAM lParam);
//键盘抬起
virtual void OnKeyUp(WPARAM wParam, LPARAM lParam);
//获得输入焦点时发生
virtual void OnFocus(HWND hWnd);
//失去输入焦点时发生
virtual void OnKillFocus(HWND hWnd);
//鼠标 键盘 重绘 会进入此函数,如果返回true则事件将不再交给sender控件处理 将忽略类似OnMouseDown... Notiify事件处理器...
virtual void OnNotify(Control* sender, EventArgs& args);
//处理消息队列的
virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
//获取阴影窗口句柄
virtual HWND GetShadowHwnd();
public:
Window(int width, int height, HWND owner = NULL, DWORD dStyle = WS_OVERLAPPEDWINDOW, DWORD dwExStyle = NULL);
virtual ~Window();
//使用id寻找控件
Control* FindControl(const UIString& objectName);
//获取公共数据
WindowData* GetPublicData();
//窗口句柄
HWND Hwnd();
//获取窗口X坐标
int X();
//获取窗口Y坐标
int Y();
// 获取窗口宽度
int Width();
// 获取窗口高度
int Height();
//获取窗口基于显示器的矩形
const Rect& GetWindowRect();
//获取客户区矩形
const Rect& GetClientRect();
//获取当前窗口dpi缩放系数
float GetScale();
//设置窗口size
void SetSize(const Size& size);
//设置窗口位置
void SetLocation(const Point& pt);
//设置窗口位置大小
void SetRect(const Rect& rect);
//设置窗口最小size
void SetMiniSize(const Size& size);
//设置窗口最大size
void SetMaxSize(const Size& size);
//设置绝对宽高
void SetFixedSize(const Size& size);
//设置窗口icon
void SetIcon(HICON icon);
//设置窗口主布局
void SetLayout(ezui::Control* layout);
//从文件中加载布局
void LoadXml(const UIString& fileName);
//从内存加载布局
void LoadXml(const char* fileData, size_t fileSize);
//获取窗口主布局
Control* GetLayout();
//设置窗口标题
void SetText(const UIString& text);
//获取窗口标题
UIString GetText();
//设置与取消窗口置顶
void SetTopMost(bool top);
//是否全屏
bool IsFullScreen();
//是否最小化
bool IsMinimized();
//窗口是否最大化
bool IsMaximized();
//窗口是否置顶
bool IsTopMost();
//操作窗口的显示
virtual void Show();
//操作窗口的显示 带参数
void Show(int cmdShow);
//隐藏窗口
virtual void Hide();
//正常显示窗口
void ShowNormal();
//关闭窗口 exitCode为退出代码
void Close(int exitCode = 0);
//模态窗口方式显示窗口(会阻塞) 请务必在窗口构造函数中传入owner窗口句柄
virtual int ShowModal(bool disableOnwer = true);
//最小化窗口
void ShowMinimized();
//最大化窗口
void ShowMaximized();
//让窗口占满当前屏幕
void ShowFullScreen();
//窗口是否显示
bool IsVisible();
//设置窗口显示/隐藏
void SetVisible(bool flag);
//使区域无效(延迟刷新)
void Invalidate();
//立即更新所有无效区域(立即刷新)
void Refresh();
//居中到屏幕
void CenterToScreen();
//参考某个窗口进行居中
void CenterToWindow(HWND wnd = NULL);
//给指定控件为焦点控件
void SetFocus(Control* ctl);
//绑定对象(跟随释放)
using Object::Attach;
//分离对象(解除跟随释放)
using Object::Detach;
//绑定图片(跟随释放)
Image* Attach(Image* img);
//分离图片(解除跟随释放)
void Detach(Image* img);
};
};

303
include/EzUI/tinystr.h Normal file
View File

@@ -0,0 +1,303 @@
/*
www.sourceforge.net/projects/tinyxml
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#pragma once
#ifndef TIXML_USE_STL
#include <assert.h>
#include <string.h>
/* The support for explicit isn't that universal, and it isn't really
required - it is used to check that the TiXmlString class isn't incorrectly
used. Be nice to old compilers and macro it here:
*/
#if defined(_MSC_VER) && (_MSC_VER >= 1200 )
// Microsoft visual studio, version 6 and higher.
#define TIXML_EXPLICIT explicit
#elif defined(__GNUC__) && (__GNUC__ >= 3 )
// GCC version 3 and higher.s
#define TIXML_EXPLICIT explicit
#else
#define TIXML_EXPLICIT
#endif
namespace ezui {
/*
TiXmlString is an emulation of a subset of the std::string template.
Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
Only the member functions relevant to the TinyXML project have been implemented.
The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
a string and there's no more room, we allocate a buffer twice as big as we need.
*/
class TiXmlString
{
public:
// The size type used
typedef size_t size_type;
// Error value for find primitive
static const size_type npos; // = -1;
// TiXmlString empty constructor
TiXmlString() : rep_(&nullrep_)
{
}
// TiXmlString copy constructor
TiXmlString(const TiXmlString& copy) : rep_(0)
{
init(copy.length());
memcpy(start(), copy.data(), length());
}
// TiXmlString constructor, based on a string
TIXML_EXPLICIT TiXmlString(const char* copy) : rep_(0)
{
init(static_cast<size_type>(strlen(copy)));
memcpy(start(), copy, length());
}
// TiXmlString constructor, based on a string
TIXML_EXPLICIT TiXmlString(const char* str, size_type len) : rep_(0)
{
init(len);
memcpy(start(), str, len);
}
// TiXmlString destructor
~TiXmlString()
{
quit();
}
TiXmlString& operator = (const char* copy)
{
return assign(copy, (size_type)strlen(copy));
}
TiXmlString& operator = (const TiXmlString& copy)
{
return assign(copy.start(), copy.length());
}
// += operator. Maps to append
TiXmlString& operator += (const char* suffix)
{
return append(suffix, static_cast<size_type>(strlen(suffix)));
}
// += operator. Maps to append
TiXmlString& operator += (char single)
{
return append(&single, 1);
}
// += operator. Maps to append
TiXmlString& operator += (const TiXmlString& suffix)
{
return append(suffix.data(), suffix.length());
}
// Convert a TiXmlString into a null-terminated char *
const char* c_str() const { return rep_->str; }
// Convert a TiXmlString into a char * (need not be null terminated).
const char* data() const { return rep_->str; }
// Return the length of a TiXmlString
size_type length() const { return rep_->size; }
// Alias for length()
size_type size() const { return rep_->size; }
// Checks if a TiXmlString is empty
bool empty() const { return rep_->size == 0; }
// Return capacity of string
size_type capacity() const { return rep_->capacity; }
// single char extraction
const char& at(size_type index) const
{
assert(index < length());
return rep_->str[index];
}
// [] operator
char& operator [] (size_type index) const
{
assert(index < length());
return rep_->str[index];
}
// find a char in a string. Return TiXmlString::npos if not found
size_type find(char lookup) const
{
return find(lookup, 0);
}
// find a char in a string from an offset. Return TiXmlString::npos if not found
size_type find(char tofind, size_type offset) const
{
if (offset >= length()) return npos;
for (const char* p = c_str() + offset; *p != '\0'; ++p)
{
if (*p == tofind) return static_cast<size_type>(p - c_str());
}
return npos;
}
void clear()
{
//Lee:
//The original was just too strange, though correct:
// TiXmlString().swap(*this);
//Instead use the quit & re-init:
quit();
init(0, 0);
}
/* Function to reserve a big amount of data when we know we'll need it. Be aware that this
function DOES NOT clear the content of the TiXmlString if any exists.
*/
void reserve(size_type cap);
TiXmlString& assign(const char* str, size_type len);
TiXmlString& append(const char* str, size_type len);
void swap(TiXmlString& other)
{
Rep* r = rep_;
rep_ = other.rep_;
other.rep_ = r;
}
private:
void init(size_type sz) { init(sz, sz); }
void set_size(size_type sz) { rep_->str[rep_->size = sz] = '\0'; }
char* start() const { return rep_->str; }
char* finish() const { return rep_->str + rep_->size; }
struct Rep
{
size_type size, capacity;
char str[1];
};
void init(size_type sz, size_type cap)
{
if (cap)
{
// Lee: the original form:
// rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
// doesn't work in some cases of new being overloaded. Switching
// to the normal allocation, although use an 'int' for systems
// that are overly picky about structure alignment.
const size_type bytesNeeded = sizeof(Rep) + cap;
const size_type intsNeeded = (bytesNeeded + sizeof(int) - 1) / sizeof(int);
rep_ = reinterpret_cast<Rep*>(new int[intsNeeded]);
rep_->str[rep_->size = sz] = '\0';
rep_->capacity = cap;
}
else
{
rep_ = &nullrep_;
}
}
void quit()
{
if (rep_ != &nullrep_)
{
// The rep_ is really an array of ints. (see the allocator, above).
// Cast it back before delete, so the compiler won't incorrectly call destructors.
delete[](reinterpret_cast<int*>(rep_));
}
}
Rep* rep_;
static Rep nullrep_;
};
inline bool operator == (const TiXmlString& a, const TiXmlString& b)
{
return (a.length() == b.length()) // optimization on some platforms
&& (strcmp(a.c_str(), b.c_str()) == 0); // actual compare
}
inline bool operator < (const TiXmlString& a, const TiXmlString& b)
{
return strcmp(a.c_str(), b.c_str()) < 0;
}
inline bool operator != (const TiXmlString& a, const TiXmlString& b) { return !(a == b); }
inline bool operator > (const TiXmlString& a, const TiXmlString& b) { return b < a; }
inline bool operator <= (const TiXmlString& a, const TiXmlString& b) { return !(b < a); }
inline bool operator >= (const TiXmlString& a, const TiXmlString& b) { return !(a < b); }
inline bool operator == (const TiXmlString& a, const char* b) { return strcmp(a.c_str(), b) == 0; }
inline bool operator == (const char* a, const TiXmlString& b) { return b == a; }
inline bool operator != (const TiXmlString& a, const char* b) { return !(a == b); }
inline bool operator != (const char* a, const TiXmlString& b) { return !(b == a); }
TiXmlString operator + (const TiXmlString& a, const TiXmlString& b);
TiXmlString operator + (const TiXmlString& a, const char* b);
TiXmlString operator + (const char* a, const TiXmlString& b);
/*
TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
Only the operators that we need for TinyXML have been developped.
*/
class TiXmlOutStream : public TiXmlString
{
public:
// TiXmlOutStream << operator.
TiXmlOutStream& operator << (const TiXmlString& in)
{
*this += in;
return *this;
}
// TiXmlOutStream << operator.
TiXmlOutStream& operator << (const char* in)
{
*this += in;
return *this;
}
};
};
#endif // TIXML_USE_STL

1806
include/EzUI/tinyxml.h Normal file

File diff suppressed because it is too large Load Diff

BIN
lib/EzUI_Debug_Win32.lib Normal file

Binary file not shown.

BIN
lib/EzUI_Debug_Win32.pdb Normal file

Binary file not shown.

BIN
lib/EzUI_Debug_x64.lib Normal file

Binary file not shown.

BIN
lib/EzUI_Debug_x64.pdb Normal file

Binary file not shown.

BIN
lib/EzUI_Release_Win32.lib Normal file

Binary file not shown.

BIN
lib/EzUI_Release_x64.lib Normal file

Binary file not shown.

65
sources/Animation.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include "Animation.h"
namespace ezui {
Animation::Animation(Object* parentObject)
: Object(parentObject)
{
m_timer = new Timer(this);
m_timer->Tick = [this](Timer* t) {
auto now = std::chrono::steady_clock::now();
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_lastTime).count();
m_lastTime = now;
m_currValue += m_speedPerMs * delta;
bool finished = false;
if ((m_speedPerMs > 0 && m_currValue >= m_endValue) || (m_speedPerMs < 0 && m_currValue <= m_endValue)) {
m_currValue = m_endValue;
finished = true;
}
if (ValueChanged) {
ValueChanged(m_currValue);
}
if (finished) {
t->Stop();
}
};
}
Animation::~Animation()
{
m_timer->Stop();
}
void Animation::SetStartValue(float value)
{
m_startValue = value;
}
void Animation::SetEndValue(float value)
{
m_endValue = value;
}
void Animation::Start(int durationMs, int fps)
{
m_timer->Stop();
m_currValue = m_startValue;
float distance = m_endValue - m_startValue;
m_speedPerMs = distance / durationMs;
m_lastTime = std::chrono::steady_clock::now(); //记录开始时间
m_timer->Interval = 1000 / fps; //频率
m_timer->Start();
}
bool Animation::IsStopped() {
return m_timer->IsStopped();
}
void Animation::Stop()
{
m_timer->Stop();
}
}

260
sources/Application.cpp Normal file
View File

@@ -0,0 +1,260 @@
#include "Application.h"
#include "LayeredWindow.h"
#include "UIManager.h"
#include <CommCtrl.h>
#undef FindResource
namespace ezui {
bool g_comInitialized = false;//标记是否由我初始化
std::list<HWND> g_hWnds;//存储所有使用本框架产生的窗口句柄
// 内部使用:枚举名称时的上下文
struct ResourceContext {
UIString rcIDName;
HRSRC hResource = NULL;
UIString rcType;
};
// 回调:枚举资源名称
BOOL CALLBACK EnumNamesProc(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam) {
ResourceContext* ctx = (ResourceContext*)(lParam);
UIString resName = UIString(lpszName).toLower();
if (!IS_INTRESOURCE(lpszName) && ctx->rcIDName.toLower() == resName) {
ctx->hResource = FindResourceW(hModule, lpszName, lpszType);
ctx->rcType = (wchar_t*)lpszType;
return FALSE; // 找到就停止
}
return TRUE; // 继续
}
//通过资源名称查找windows嵌套资源
HRSRC FindResource(const UIString& rcIDName)
{
HMODULE hModule = ezui::__EzUI__HINSTANCE;
ResourceContext ctx;
ctx.rcIDName = rcIDName;
EnumResourceTypesW(hModule, [](HMODULE hModule, LPWSTR lpszType, LONG_PTR lParam)->BOOL {
return EnumResourceNamesW(hModule, lpszType, EnumNamesProc, lParam);
}, (LONG_PTR)&ctx);
return ctx.hResource;
}
}
namespace ezui {
LRESULT CALLBACK __EzUI__WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_CREATE) {
g_hWnds.push_back(hWnd);
}
else if (message == WM_DESTROY) {
auto itor = std::find(g_hWnds.begin(), g_hWnds.end(), hWnd);
if (itor != g_hWnds.end()) {
g_hWnds.erase(itor);
}
}
WindowData* wndData = (WindowData*)UI_GET_USERDATA(hWnd);
//执行消息过程
if (wndData && wndData->WndProc) {
return wndData->WndProc(hWnd, message, wParam, lParam);
}
return ::DefWindowProc(hWnd, message, wParam, lParam);
}
void InitInvoker() {
if (!ezui::__EzUI_MessageWnd) {
::WNDCLASSEXW wcex = {};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -> LRESULT {
if (msg == WM_GUI_SYSTEM) {
if (wParam == WM_GUI_BEGININVOKE || wParam == WM_GUI_INVOKE) {
using Func = std::function<void()>;
Func* callback = (Func*)lParam;
(*callback)();
if (wParam == WM_GUI_BEGININVOKE) {
delete callback;
}
}
return 0;
}
return ::DefWindowProc(hwnd, msg, wParam, lParam);
};
wcex.hInstance = ezui::__EzUI__HINSTANCE;
wcex.lpszClassName = EZUI_INVOKER_WINDOW_CLASS;
RegisterClassExW(&wcex);
ezui::__EzUI_MessageWnd = CreateWindowEx(
WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW,
EZUI_INVOKER_WINDOW_CLASS, // 上面注册的类名
L"", // 窗口名(无用)
0, // 样式设为 0无 WS_VISIBLE
0, 0, 0, 0, // 尺寸全为 0
NULL, NULL, ezui::__EzUI__HINSTANCE, NULL
);
}
}
//销毁多线程通讯窗口
void DestroyInvoker() {
//销毁用于通讯的不可见窗口
if (ezui::__EzUI_MessageWnd) {
::DestroyWindow(ezui::__EzUI_MessageWnd);
ezui::__EzUI_MessageWnd = NULL;
BOOL ret = UnregisterClassW(EZUI_INVOKER_WINDOW_CLASS, ezui::__EzUI__HINSTANCE);
}
}
void Application::EnableHighDpi() {
ezui::GetMonitor((std::list<MonitorInfo>*) & ezui::__EzUI__MonitorInfos);//获取一次显示器信息
//DPI感知相关
//不跟随系统放大无法接收WM_DISPLAYCHANGED消息
//bool b = SetProcessDPIAware();
//SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
//会进入WM_DPICHANGED消息可进行自行控制缩放
HMODULE hModNtdll = NULL;
if (hModNtdll = ::LoadLibraryW(L"User32.dll")) {
typedef void (WINAPI* Func_SetProcessDpiAwarenessContext)(void*);
Func_SetProcessDpiAwarenessContext setProcessDpiAwarenessContext;
setProcessDpiAwarenessContext = (Func_SetProcessDpiAwarenessContext)::GetProcAddress(hModNtdll, "SetProcessDpiAwarenessContext");
if (setProcessDpiAwarenessContext)
{
//SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);//高版本sdk直接用
setProcessDpiAwarenessContext((void*)-4);//函数指针方式是为了兼容win7
}
else {
::SetProcessDPIAware();
}
::FreeLibrary(hModNtdll);
}
}
bool Application::SetResource(const UIString& localOrResName) {
//只允许有一个资源文件
if (ezui::__EzUI__Resource) {
delete ezui::__EzUI__Resource;
ezui::__EzUI__Resource = NULL;
}
//先从vs中的资源里面查找
bool found = false;
HRSRC hRsrc = ezui::FindResource(localOrResName);
if (hRsrc) {
ezui::__EzUI__Resource = new Resource(hRsrc);
found = true;
}
else {
//本地文件中获取
std::wstring wstr = localOrResName.unicode();
DWORD dwAttr = GetFileAttributesW(wstr.c_str());
if (dwAttr && (dwAttr != -1) && (dwAttr & FILE_ATTRIBUTE_ARCHIVE)) {
ezui::__EzUI__Resource = new Resource(localOrResName);
found = true;
}
}
#ifdef _DEBUG
if (!found) {
::MessageBoxW(NULL, localOrResName.unicode().c_str(), L"Failed to open Resource", MB_OK | MB_ICONINFORMATION);
}
#endif
return found;
}
Application::Application(HINSTANCE hInstance) {
//存入全局实例
ezui::__EzUI__HINSTANCE = hInstance == NULL ? ::GetModuleHandleW(NULL) : hInstance;
ezui::__EzUI__ThreadId = ::GetCurrentThreadId();
//设计窗口
::WNDCLASSEXW wcex = {};
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;//宽高改变窗口触发重绘
wcex.lpfnWndProc = __EzUI__WndProc;//窗口过程
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = ezui::__EzUI__HINSTANCE;//
wcex.hIcon = NULL;//(大图标)用于窗口标题栏、Alt+Tab 切换界面、任务栏预览等显示大图标的地方
wcex.hIconSm = NULL;//(小图标)用于任务栏按钮、系统托盘(部分情况下)以及 Alt+Tab 小图标显示等
wcex.hCursor = LoadCursorW(NULL, IDC_ARROW);//光标
wcex.hbrBackground = NULL; // 窗口背景
wcex.lpszMenuName = NULL;
wcex.lpszClassName = EZUI_WINDOW_CLASS;//类名
if (!RegisterClassExW(&wcex)) //注册窗口
{
::MessageBoxW(NULL, L"This program requires Windows NT !",
wcex.lpszClassName, MB_ICONERROR);
return;
}
//注册一个窗口类并创建隐形窗口用于UI直接的多线程通讯
InitInvoker();
//为程序设置工作目录
std::wstring startPath = Application::StartPath().unicode();
::SetCurrentDirectoryW(startPath.c_str());
if (!g_comInitialized) {
auto ret = ::CoInitialize(NULL);//初始化com
if (ret == S_OK) {
g_comInitialized = true;//由我初始化的
}
}
RenderInitialize();//初始化图形绘制库 D2D/GDI/GDI+
InitControls();//注册基础控件
}
//销毁所有窗口
void DestroyAllWindows() {
while (ezui::g_hWnds.size() > 0)
{
auto itor = ezui::g_hWnds.begin();
if (itor != ezui::g_hWnds.end()) {
HWND hwnd = *itor;
g_hWnds.erase(itor);
::DestroyWindow(hwnd);
}
}
}
Application::~Application() {
//销毁通讯窗口
DestroyInvoker();
//销毁所有窗口
DestroyAllWindows();
//取消窗口注册的类
BOOL ret = UnregisterClassW(EZUI_WINDOW_CLASS, ezui::__EzUI__HINSTANCE);
RenderUnInitialize();
if (g_comInitialized) {
::CoUninitialize();
g_comInitialized = false;
}
if (ezui::__EzUI__Resource) {
delete ezui::__EzUI__Resource;
ezui::__EzUI__Resource = NULL;
}
}
int Application::Exec()
{
MSG msg;
while (::GetMessage(&msg, NULL, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return msg.wParam;
}
void Application::Exit(int exitCode) {
//销毁用于通讯的不可见窗口
DestroyInvoker();
//销毁所有窗口
DestroyAllWindows();
//退出消息循环
::PostQuitMessage(exitCode);
}
UIString Application::StartPath()
{
std::vector<wchar_t> wPath(32768);
DWORD count = ::GetModuleFileNameW(__EzUI__HINSTANCE, wPath.data(), (DWORD)wPath.size());
for (int i = count - 1; i > -1; i--)
{
if (wPath[i] == L'\\') {
wPath[i] = 0;
break;
}
}
return UIString(wPath.data());
}
};

180
sources/Bitmap.cpp Normal file
View File

@@ -0,0 +1,180 @@
#include "Bitmap.h"
#include <gdiplus.h>
namespace ezui {
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0) {
return -1; // Failure
}
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL) {
return -1; // Failure
}
Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
Bitmap::Bitmap(int width, int height) {
Create(width, height);
}
Bitmap::Bitmap(HDC dc, const Rect& rect) {
Create(rect.Width, rect.Height);
::BitBlt(this->GetDC(), 0, 0, rect.Width, rect.Height, dc, rect.X, rect.Y, SRCCOPY);
}
void Bitmap::Create(int width, int height) {
m_width = width;
m_height = height;
memset(&m_bmpInfo, 0, sizeof(m_bmpInfo));
BITMAPINFOHEADER& bmih = m_bmpInfo.bmiHeader;
bmih.biSize = sizeof(bmih);
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
bmih.biPlanes = 1;
bmih.biWidth = width;
bmih.biHeight = -height;
bmih.biSizeImage = width * height * 4;
m_bmp = ::CreateDIBSection(NULL, &m_bmpInfo, DIB_RGB_COLORS, (void**)&m_point, NULL, 0);
::memset(m_point, 0, bmih.biSizeImage);
this->GetDC();
}
int Bitmap::Width()const {
return m_width;
}
int Bitmap::Height() const {
return m_height;
}
void Bitmap::SetPixel(int x, int y, const Color& color) {
DWORD* point = (DWORD*)this->m_point + (x + y * this->Width());//起始地址+坐标偏移
((BYTE*)point)[3] = color.GetA();//修改A通道数值
((BYTE*)point)[2] = color.GetR();//修改R通道数值
((BYTE*)point)[1] = color.GetG();//修改G通道数值
((BYTE*)point)[0] = color.GetB();//修改B通道数值
}
Color Bitmap::GetPixel(int x, int y) const {
DWORD* point = (DWORD*)this->m_point + (x + y * this->Width());//起始地址+坐标偏移
BYTE a, r, g, b;
a = ((BYTE*)point)[3];
r = ((BYTE*)point)[2];//修改R通道数值
g = ((BYTE*)point)[1];//修改G通道数值
b = ((BYTE*)point)[0];//修改B通道数值
return Color(r, g, b, a);
}
byte* Bitmap::GetPixel()
{
return (byte*)this->m_point;
}
void Bitmap::Earse(const Rect& _rect) {
Rect rect = _rect;
if (rect.X < 0) {
rect.X = 0;
rect.Width += rect.X;
}
if (rect.Y < 0) {
rect.Y = 0;
rect.Height += rect.Y;
}
if (rect.GetBottom() > Height()) {
rect.Height = this->Height() - rect.Y;
}
if (rect.GetRight() > Width()) {
rect.Width = this->Width() - rect.X;
}
for (int y = rect.Y; y < rect.GetBottom(); ++y)
{
DWORD* point = (DWORD*)this->m_point + (rect.X + y * this->Width());//起始地址+坐标偏移
::memset(point, 0, rect.Width * 4);//抹除
}
}
HBITMAP Bitmap::GetHBITMAP()
{
return this->m_bmp;
}
HDC Bitmap::GetDC() {
if (!m_hdc) {
m_hdc = ::CreateCompatibleDC(NULL);
::SelectObject(m_hdc, m_bmp);
}
return m_hdc;
}
bool Bitmap::Save(const UIString& fileName)
{
size_t pos = fileName.rfind(".");
UIString ext;
if (pos != size_t(-1)) {
ext = fileName.substr(pos);
ext = ext.toLower();
}
bool ret = false;
Gdiplus::Bitmap* pbmSrc = NULL;
do
{
CLSID pngClsid;
int code = -1;
if (ext == ".png") {
code = GetEncoderClsid(L"image/png", &pngClsid);
}
else if (ext == ".jpg" || ext == ".jpeg") {
code = GetEncoderClsid(L"image/jpeg", &pngClsid);
}
else if (ext == ".tiff") {
code = GetEncoderClsid(L"image/tiff", &pngClsid);
}
else {
code = GetEncoderClsid(L"image/bmp", &pngClsid);
}
if (code == -1) {
break;
}
pbmSrc = new Gdiplus::Bitmap(Width(), Height(), Width() * 4, PixelFormat32bppARGB, (BYTE*)GetPixel());
if (pbmSrc && pbmSrc->Save(fileName.unicode().c_str(), &pngClsid) == Gdiplus::Status::Ok)
{
ret = true;
break;
}
} while (false);
if (pbmSrc) {
delete pbmSrc;
}
return ret;
}
Bitmap* Bitmap::Clone() const {
if (m_width <= 0 || m_height <= 0) {
return NULL;
}
// 创建新 Bitmap 对象(保留格式)
Bitmap* clone = new Bitmap(m_width, m_height);
//拷贝像素
memcpy(clone->m_point, m_point, m_width * m_height * 4);
return clone;
}
Bitmap::~Bitmap() {
if (m_hdc) {
::DeleteDC(m_hdc);
::DeleteObject((HGDIOBJ)(HBITMAP)(m_bmp));
}
}
};

View File

@@ -0,0 +1,173 @@
#include "BorderlessWindow.h"
namespace ezui {
BorderlessWindow::BorderlessWindow(int width, int height, HWND owner, DWORD dwStyle, DWORD dwExStyle) : Window(width, height, owner, dwStyle | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_POPUP, dwExStyle)
{
//无边框
auto style = ::GetWindowLong(Hwnd(), GWL_STYLE);
style |= WS_THICKFRAME;
::SetWindowLong(Hwnd(), GWL_STYLE, style);
m_shadowBox = new ShadowBox(width, height, Hwnd());
UpdateShadowBox();
}
BorderlessWindow::~BorderlessWindow() {
CloseShadowBox();
}
ShadowBox* BorderlessWindow::GetShadowBox()
{
return m_shadowBox;
}
void BorderlessWindow::SetShadow(int padding)
{
m_shadowWeight = padding;
UpdateShadowBox();
}
void BorderlessWindow::OnDpiChange(float systemScale, const Rect& newRect)
{
if (m_shadowBox) {//对窗口阴影进行新DPI适配
if (this->m_shadowScale != systemScale) {
this->m_shadowWeight *= systemScale / m_shadowScale;
this->m_shadowScale = systemScale;
UpdateShadowBox();
}
}
//对边框进行新DPI适配
float newScale = systemScale / GetPublicData()->Scale;
__super::OnDpiChange(systemScale, newRect);
}
void BorderlessWindow::SetResizable(bool resize) {
this->m_bResize = resize;
}
bool BorderlessWindow::IsResizable() {
return this->m_bResize;
}
HWND BorderlessWindow::GetShadowHwnd()
{
if (m_shadowBox) {
return m_shadowBox->Hwnd();
}
return NULL;
}
void BorderlessWindow::UpdateShadowBox() {
if (m_shadowBox && ::IsWindow(m_shadowBox->Hwnd())) {
auto* mainLayout = this->GetLayout();
int shadowWeight = m_shadowWeight;
m_shadowBox->Update(shadowWeight * this->GetScale(), (mainLayout ? mainLayout->GetBorderTopLeftRadius() : 0));
}
}
void BorderlessWindow::CloseShadowBox()
{
if (m_shadowBox) {
delete m_shadowBox;
m_shadowBox = NULL;
}
}
LRESULT BorderlessWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg)
{
case WM_NCCALCSIZE:
{
if (wParam == TRUE) {
return 0;
}
break;
}
case WM_NCPAINT:
{
return 0;
}
case WM_NCACTIVATE:
{
return (wParam == FALSE ? TRUE : FALSE);
}
case WM_WINDOWPOSCHANGED:
{
WINDOWPOS* pPos = (WINDOWPOS*)lParam;
//在窗口显示/隐藏的时候更新阴影窗口
if ((pPos->flags & SWP_SHOWWINDOW) || (pPos->flags & SWP_HIDEWINDOW)) {
this->UpdateShadowBox();
}
// Z-order 发生变化
if ((pPos->flags & SWP_NOZORDER) == 0) {
this->UpdateShadowBox();
}
break;
}
case WM_ACTIVATE: {
auto state = LOWORD(wParam);
if (state == WA_ACTIVE || state == WA_CLICKACTIVE) {
// 窗口激活
}
else if (state == WA_INACTIVE) {
// 窗口失活
}
this->UpdateShadowBox();
break;
}
case WM_SHOWWINDOW: {
//窗口的可见状态标志被设置(此时窗口还未真正生效)
if (wParam == TRUE) {
//SHOW WINDOWS
}
else {
//HIDE WINDOWS
}
break;
}
case WM_NCHITTEST:
{
//非全屏状态下/非最大化状态下/当调整大小的标志为true 的情况下才允许调整大小;
if (!IsFullScreen() && !IsMaximized() && this->IsResizable()) {
RECT rc;
::GetWindowRect(Hwnd(), &rc);
POINT pt{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) };
int x = 4;//
if (pt.x < rc.left + x)
{
if (pt.y < rc.top + x)return HTTOPLEFT;//
if (pt.y >= rc.bottom - x)return HTBOTTOMLEFT;//
return HTLEFT;//
}
if (pt.x >= rc.right - x)//
{
if (pt.y < rc.top + x)return HTTOPRIGHT;//
if (pt.y >= rc.bottom - x)return HTBOTTOMRIGHT;//
return HTRIGHT;//
}
if (pt.y < rc.top + x)return HTTOP;//
if (pt.y >= rc.bottom - x)return HTBOTTOM;//
return HTCLIENT;//
}
break;
}
case WM_KILLFOCUS: {
HWND wnd = (HWND)wParam;
if (this->GetShadowBox() && wnd != this->GetShadowBox()->Hwnd()) {
this->OnKillFocus(wnd);
}
else if (this->GetShadowBox() == NULL) {
this->OnKillFocus(wnd);
}
return 0;
}
default:
break;
}
return __super::WndProc(uMsg, wParam, lParam);
}
void BorderlessWindow::OnMove(const Point& location)
{
__super::OnMove(location);
UpdateShadowBox();
}
void BorderlessWindow::OnSize(const Size& sz)
{
__super::OnSize(sz);//避免先显示阴影再显示窗口的问题
UpdateShadowBox();
}
}

15
sources/Button.cpp Normal file
View File

@@ -0,0 +1,15 @@
#include "Button.h"
namespace ezui {
void Button::Init()
{
Style.Cursor = LoadCursor(Cursor::HAND);
}
Button::Button(Object* parentObject):Label(parentObject)
{
Init();
}
Button::~Button()
{
}
};

83
sources/CheckBox.cpp Normal file
View File

@@ -0,0 +1,83 @@
#include "CheckBox.h"
namespace ezui {
CheckBox::CheckBox(Object* parentObject) :Label(parentObject)
{
}
CheckBox::~CheckBox()
{
}
void CheckBox::SetCheck(bool checked)
{
m_checked = checked;
if (checked) {
this->State = this->State | ControlState::Checked;
}
else {
this->State = this->State & (~ControlState::Checked);
}
}
bool CheckBox::GetCheck()
{
return m_checked;
}
void CheckBox::OnDpiChange(const DpiChangeEventArgs& args) {
if (!ezui::IsFloatEqual(args.Scale, this->GetScale())) {
this->CheckedStyle.Scale(args.Scale / this->GetScale());
}
__super::OnDpiChange(args);
}
void CheckBox::SetAttribute(const UIString& key, const UIString& value)
{
__super::SetAttribute(key, value);
if (key == "checked") {
if (value == "true") {
SetCheck(true);
return;
}
if (value == "false") {
SetCheck(false);
return;
}
}
}
ControlStyle& CheckBox::GetStyle(const ControlState& _state) {
if ((_state & ControlState::Checked) == ControlState::Checked) {
return this->CheckedStyle;
}
return __super::GetStyle(_state);
}
void CheckBox::OnMouseDown(const MouseEventArgs& arg) {
__super::OnMouseDown(arg);
SetCheck(!GetCheck());
if (CheckedChanged) {
CheckedChanged(this, GetCheck());
}
if (GetCheck()) {
this->State = this->State | ControlState::Checked;
this->Invalidate();
}
}
void CheckBox::OnMouseEnter(const MouseEventArgs& args)
{
__super::OnMouseEnter(args);
if (GetCheck()) {
this->State = this->State | ControlState::Checked;
this->Invalidate();
}
}
void CheckBox::OnMouseUp(const MouseEventArgs& args)
{
__super::OnMouseUp(args);
}
void CheckBox::OnMouseLeave(const MouseEventArgs& args)
{
__super::OnMouseLeave(args);
if (GetCheck()) {
this->State = this->State | ControlState::Checked;
this->Invalidate();
}
}
};

152
sources/ComboBox.cpp Normal file
View File

@@ -0,0 +1,152 @@
#include "ComboBox.h"
namespace ezui {
Control* ComboBox::Add(Control* childCtl)
{
return __super::Add(childCtl);
}
void ComboBox::Remove(Control* childCtl, bool freeCtrl)
{
__super::Remove(childCtl, freeCtrl);
}
void ComboBox::Init()
{
this->m_textBox.SetReadOnly(true);
this->Add(&m_textBox);
this->Add(&m_UpDown);
m_UpDown.EventHandler = [&](Control* sd, EventArgs& arg)->void {
if (arg.EventType == Event::OnPaint) {
//绘制
auto& args = (PaintEventArgs&)arg;
auto fSzie = sd->GetFontSize() * 0.5f;
int width = fSzie * 1.5f;
Rect rect(0, 0, width, fSzie);
rect.Y = (sd->Height() - fSzie) / 2.0f;
rect.X += (sd->Height() - width) / 2.0f;
//args.Graphics.SetColor(Color::Red);
//args.Graphics.DrawRectangle(rect);
PointF p1(rect.GetLeft(), rect.Y);
PointF p2(rect.GetRight(), rect.Y);
PointF p3(rect.GetLeft() + width / 2.0f, rect.GetBottom());
args.Graphics.SetColor(this->GetForeColor());
args.Graphics.DrawLine(p1, p3);
args.Graphics.DrawLine(p2, p3);
}
else if (arg.EventType == Event::OnMouseDown/*&& args.Button == MouseButton::Left*/) {
//单击
if (m_menuWnd == NULL) {
m_menuWnd = new MenuContent(this, &m_UpDown);
m_menuWnd->SetShadow(10);
m_list.Style.BackColor = Color::White;
m_menuWnd->SetLayout(&m_list);
}
for (auto& it : m_list.GetControls()) {
it->SetFixedHeight(Height());
}
int height = this->Height() * m_list.GetControls().size();
if (height == 0) {
height = Height();
}
if (!m_menuWnd->IsVisible()) {
m_menuWnd->SetSize({ Width(), height });
m_menuWnd->Show();
}
else {
m_menuWnd->Hide();
}
}
};
}
ComboBox::ComboBox(Object* parentObject) :HLayout(parentObject)
{
Init();
}
UIString ComboBox::GetText()
{
return this->m_textBox.GetText();
}
int ComboBox::GetCheck()
{
return this->m_index;
}
bool ComboBox::SetCheck(int pos)
{
auto item = m_list.GetControl(pos);
if (item) {
m_textBox.SetText(((Label*)item)->GetText());
m_index = pos;
return true;
}
return false;
}
ComboBox::~ComboBox()
{
m_list.Clear(true);
if (m_menuWnd) {
delete m_menuWnd;
}
}
int ComboBox::AddItem(const UIString& text)
{
Label* lb = new Label;
lb->SetDockStyle(DockStyle::Horizontal);
lb->SetText(text);
m_list.Add(lb);
lb->HoverStyle.BackColor = Color::Gray;
lb->HoverStyle.ForeColor = Color::White;
lb->EventHandler = [&](Control* sd, const EventArgs& args) ->void {
if (args.EventType == Event::OnMouseDown) {
m_index = sd->Parent->IndexOf(sd);
m_textBox.SetText(((Label*)sd)->GetText());
m_textBox.Invalidate();
m_menuWnd->Hide();
}
};
return m_list.GetControls().size() - 1;
}
void ComboBox::RemoveItem(int index)
{
Control* lb = m_list.GetControl(index);
m_list.Remove(lb, true);
}
void ComboBox::OnLayout() {
this->m_UpDown.SetFixedSize(Size(Height(), Height()));
__super::OnLayout();
}
ComboBox::MenuContent::MenuContent(Control* ownerCtl, Control* hittestCtl) :PopupWindow(0, 0, ownerCtl), m_hittestCtl(hittestCtl)
{
}
void ComboBox::MenuContent::OnKillFocus(HWND wnd)
{
if (::GetWindow(Hwnd(), GW_OWNER) == wnd) {
POINT pt;
::GetCursorPos(&pt);
// 将鼠标屏幕坐标转换为客户端坐标
::ScreenToClient(::GetWindow(Hwnd(), GW_OWNER), &pt);
Rect _hittestRect = m_hittestCtl->GetClientRect();
if (_hittestRect.Contains(pt.x, pt.y)) {
return;
}
else {
this->Hide();
}
}
else {
this->Hide();
}
}
ComboBox::MenuContent::~MenuContent()
{
}
}

1792
sources/Control.cpp Normal file

File diff suppressed because it is too large Load Diff

1272
sources/Direct2DRender.cpp Normal file

File diff suppressed because it is too large Load Diff

605
sources/EzUI.cpp Normal file
View File

@@ -0,0 +1,605 @@
#include "EzUI.h"
#include "Bitmap.h"
#include "Control.h"
#include "IFrame.h"
#include "Window.h"
#include "TabLayout.h"
#include <gdiplus.h>
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib,"imm32.lib")
namespace ezui {
HMODULE __EzUI__HINSTANCE = NULL;
Resource* __EzUI__Resource = NULL;
DWORD __EzUI__ThreadId = NULL;
HWND __EzUI_MessageWnd = NULL;
const std::list<ezui::MonitorInfo> __EzUI__MonitorInfos;
Color Color::Make(const UIString& colorStr, bool* _isGood) {
Color color;
bool _bCopy;
bool* isGood = _isGood ? _isGood : &_bCopy;
*isGood = false;
do {
if (colorStr == "black") {
color = Color::Black; *isGood = true; break;
}
else if (colorStr == "white") {
color = Color::White; *isGood = true; break;
}
else if (colorStr == "gray") {
color = Color::Gray; *isGood = true; break;
}
else if (colorStr == "lightgray") {
color = Color::LightGray; *isGood = true; break;
}
else if (colorStr == "darkgray") {
color = Color::DarkGray; *isGood = true; break;
}
else if (colorStr == "red") {
color = Color::Red; *isGood = true; break;
}
else if (colorStr == "darkred") {
color = Color::DarkRed; *isGood = true; break;
}
else if (colorStr == "lightcoral") {
color = Color::LightCoral; *isGood = true; break;
}
else if (colorStr == "tomato") {
color = Color::Tomato; *isGood = true; break;
}
else if (colorStr == "crimson") {
color = Color::Crimson; *isGood = true; break;
}
else if (colorStr == "green" || colorStr == "lime") {
color = Color::Green; *isGood = true; break;
}
else if (colorStr == "darkgreen") {
color = Color::DarkGreen; *isGood = true; break;
}
else if (colorStr == "lawngreen") {
color = Color::LawnGreen; *isGood = true; break;
}
else if (colorStr == "palegreen") {
color = Color::PaleGreen; *isGood = true; break;
}
else if (colorStr == "blue") {
color = Color::Blue; *isGood = true; break;
}
else if (colorStr == "royalblue") {
color = Color::RoyalBlue; *isGood = true; break;
}
else if (colorStr == "dodgerblue") {
color = Color::DodgerBlue; *isGood = true; break;
}
else if (colorStr == "deepskyblue") {
color = Color::DeepSkyBlue; *isGood = true; break;
}
else if (colorStr == "lightblue") {
color = Color::LightBlue; *isGood = true; break;
}
else if (colorStr == "yellow") {
color = Color::Yellow; *isGood = true; break;
}
else if (colorStr == "gold") {
color = Color::Gold; *isGood = true; break;
}
else if (colorStr == "lightyellow") {
color = Color::LightYellow; *isGood = true; break;
}
else if (colorStr == "khaki") {
color = Color::Khaki; *isGood = true; break;
}
else if (colorStr == "orange") {
color = Color::Orange; *isGood = true; break;
}
else if (colorStr == "darkorange") {
color = Color::DarkOrange; *isGood = true; break;
}
else if (colorStr == "coral") {
color = Color::Coral; *isGood = true; break;
}
else if (colorStr == "salmon") {
color = Color::Salmon; *isGood = true; break;
}
else if (colorStr == "purple") {
color = Color::Purple; *isGood = true; break;
}
else if (colorStr == "mediumpurple") {
color = Color::MediumPurple; *isGood = true; break;
}
else if (colorStr == "indigo") {
color = Color::Indigo; *isGood = true; break;
}
else if (colorStr == "violet") {
color = Color::Violet; *isGood = true; break;
}
else if (colorStr == "plum") {
color = Color::Plum; *isGood = true; break;
}
else if (colorStr == "cyan" || colorStr == "aqua") {
color = Color::Cyan; *isGood = true; break;
}
else if (colorStr == "teal") {
color = Color::Teal; *isGood = true; break;
}
else if (colorStr == "turquoise") {
color = Color::Turquoise; *isGood = true; break;
}
else if (colorStr == "brown") {
color = Color::Brown; *isGood = true; break;
}
else if (colorStr == "maroon") {
color = Color::Maroon; *isGood = true; break;
}
else if (colorStr == "tan") {
color = Color::Tan; *isGood = true; break;
}
else if (colorStr == "beige") {
color = Color::Beige; *isGood = true; break;
}
else if (colorStr == "navy") {
color = Color::Navy; *isGood = true; break;
}
else if (colorStr == "olive") {
color = Color::Olive; *isGood = true; break;
}
else if (colorStr == "silver") {
color = Color::Silver; *isGood = true; break;
}
else if (colorStr == "transparent") {
color = Color::Transparent; *isGood = true; break;
}
else if (colorStr.find("#") == 0 && colorStr.size() == 7) { //"#4e6ef2"
auto rStr = colorStr.substr(1, 2);
auto gStr = colorStr.substr(3, 2);
auto bStr = colorStr.substr(5, 2);
DWORD r, g, b;
sscanf_s(rStr.c_str(), "%x", &r);
sscanf_s(gStr.c_str(), "%x", &g);
sscanf_s(bStr.c_str(), "%x", &b);
color = Color((BYTE)r, (BYTE)g, (BYTE)b);
*isGood = true; break;
}
else if (colorStr.find("rgb") == 0) { //"rgb(255,255,255,50%)"
size_t pos1 = colorStr.find("(");
size_t pos2 = colorStr.rfind(")");
if (pos1 == std::string::npos || pos2 == std::string::npos) {
break;//非标准rgb格式
}
UIString rgbStr = colorStr.substr(pos1 + 1, pos2 - pos1 - 1);
auto rgbList = rgbStr.replace(' ', ',').split(",");
if (rgbList.size() < 3) {
break;//非标准rgb格式
}
BYTE r = std::stoi(rgbList.at(0));
BYTE g = std::stoi(rgbList.at(1));
BYTE b = std::stoi(rgbList.at(2));
BYTE a = 255;
if (rgbList.size() > 3) { //如果有设置透明度
std::string aStr = rgbList.at(3);
if (aStr.rfind("%") != std::string::npos) {
a = (BYTE)(std::atof(aStr.c_str()) / 100.0f * 255 + 0.5);//计算出透明度转为byte
}
else {
a = std::stoi(aStr.c_str());//0~255描述透明度
}
}
color = Color(r, g, b, a);
*isGood = true; break;
}
} while (false);
return color;
}
Image::Image(Bitmap* bitmap) :DXImage(bitmap->GetHBITMAP())
{
}
Image* Image::Make(const UIString& fileOrRes) {
//本地文件中获取
std::wstring wstr = fileOrRes.unicode();
DWORD dwAttr = GetFileAttributesW(wstr.c_str());
if (dwAttr && (dwAttr != -1) && (dwAttr & FILE_ATTRIBUTE_ARCHIVE)) {
Image* img = new Image(wstr);
#ifdef _DEBUG
if (img) {
img->m_path = fileOrRes;
}
#endif
return img;
}
//从资源中获取
if (ezui::__EzUI__Resource) {
std::string data;
if (!ezui::__EzUI__Resource->GetFile(fileOrRes, &data) || data.empty()) {
return NULL;
}
Image* img = new Image(data.c_str(), data.size());
#ifdef _DEBUG
if (img) {
img->m_path = fileOrRes;
}
#endif
return img;
}
return NULL;
}
bool IsFloatEqual(float num1, float num2)
{
return std::fabsf(num1 - num2) <= EZUI_FLOAT_EPSILON;
}
HICON LoadIcon(const UIString& fileName)
{
std::string fileData;
if (!GetResource(fileName, &fileData)) {
return NULL;
}
const char* pData = fileData.c_str();
size_t size = fileData.size();
HICON hIcon = NULL;
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, size);
void* pMem = ::GlobalLock(hMem);
::memcpy(pMem, pData, size);
::GlobalUnlock(hMem);
IStream* pStream = NULL;
if (::CreateStreamOnHGlobal(hMem, TRUE, &pStream) != S_OK) {
return NULL;
}
Gdiplus::Bitmap* bmp = Gdiplus::Bitmap::FromStream(pStream);
if (bmp && bmp->GetHICON(&hIcon) != Gdiplus::Ok) {
hIcon = NULL;
}
delete bmp;
pStream->Release();
return hIcon;
}
void InstallFont(const UIString& fontFileName) {
auto ret = ::AddFontResourceW(fontFileName.unicode().c_str());
::SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, 0, NULL, SPIF_SENDCHANGE);//刷新
}
void UnstallFont(const UIString& fontFileName) {
auto ret = ::RemoveFontResourceW(fontFileName.unicode().c_str());
::SystemParametersInfoW(SPI_SETNONCLIENTMETRICS, 0, NULL, SPIF_SENDCHANGE);//刷新
}
bool CopyToClipboard(int uFormat, void* pData, size_t size, HWND hWnd) {
//打开剪贴板
bool ret = ::OpenClipboard(hWnd);
if (!ret)return ret;
//清空剪贴板
::EmptyClipboard();
//为剪切板申请内存
HGLOBAL clip = ::GlobalAlloc(GMEM_DDESHARE, size);
::memcpy((void*)clip, pData, size);
//解锁
ret = ::GlobalUnlock(clip);
ret = ::SetClipboardData(uFormat, clip);
ret = ::CloseClipboard();
return ret;
}
bool GetClipboardData(int uFormat, std::function<void(void*, size_t)> Callback, HWND hWnd) {
//只接收当前类型
bool ret = ::IsClipboardFormatAvailable(uFormat);
if (!ret)return ret;
//打开剪贴版
ret = OpenClipboard(hWnd);
if (!ret)return ret;
//获取剪贴板数据
HANDLE hClipboard = ::GetClipboardData(uFormat);
size_t dataSize = ::GlobalSize(hClipboard);
void* pData = ::GlobalLock(hClipboard);
if (Callback) {
Callback(pData, dataSize);
}
//解锁
ret = ::GlobalUnlock(hClipboard);
ret = ::CloseClipboard();
return ret;
}
bool CopyToClipboard(const std::wstring& str, HWND hWnd) {
return CopyToClipboard(CF_UNICODETEXT, (void*)str.c_str(), (str.size() + 1) * 2, hWnd);
}
bool GetClipboardData(std::wstring* outStr, HWND hWnd) {
bool ret = GetClipboardData(CF_UNICODETEXT, [=](void* data, size_t _sz) {
wchar_t* wstr = (wchar_t*)data;
size_t sz = _sz / 2;
outStr->clear();
outStr->append(wstr, wstr[sz - 1] == 0 ? sz - 1 : sz);
}, hWnd);
return ret;
}
bool GetResource(const UIString& filename, std::string* outFileData)
{
outFileData->clear();
//本地文件中获取
std::wstring wstr = filename.unicode();
DWORD dwAttr = GetFileAttributesW(wstr.c_str());
if (dwAttr && (dwAttr != -1) && (dwAttr & FILE_ATTRIBUTE_ARCHIVE)) {
std::ifstream ifs(wstr, std::ios::binary);
ifs.seekg(0, std::ios::end);
auto size = ifs.tellg();
outFileData->resize(size);
ifs.seekg(0);
ifs.read((char*)outFileData->c_str(), size);
ifs.close();
return true;
}
//从资源中获取
if (ezui::__EzUI__Resource) {
if (ezui::__EzUI__Resource->GetFile(filename, outFileData)) {
return true;
}
}
return false;
}
#undef GetMonitorInfo
void GetMonitorInfo(MonitorInfo& mt, HMONITOR hMonitor) {
//获取显示器信息
MONITORINFOEX infoEx;
infoEx.cbSize = sizeof(infoEx);
::GetMonitorInfoW(hMonitor, &infoEx);
mt.Monitor = hMonitor;
//逻辑宽高
mt.Rect.X = infoEx.rcMonitor.left;
mt.Rect.Y = infoEx.rcMonitor.top;
mt.Rect.Width = infoEx.rcMonitor.right - infoEx.rcMonitor.left;
mt.Rect.Height = infoEx.rcMonitor.bottom - infoEx.rcMonitor.top;
if ((infoEx.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY) {//是否为主显示器
mt.Primary = true;
}
//获取工作区域 自动排除任务栏
mt.WorkRect.X = infoEx.rcWork.left;
mt.WorkRect.Y = infoEx.rcWork.top;
mt.WorkRect.Width = infoEx.rcWork.right - infoEx.rcWork.left;
mt.WorkRect.Height = infoEx.rcWork.bottom - infoEx.rcWork.top;
//获取物理宽高
DEVMODE dm;
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
::EnumDisplaySettings(infoEx.szDevice, ENUM_REGISTRY_SETTINGS, &dm);
mt.Physical.Width = dm.dmPelsWidth;//物理宽
mt.Physical.Height = dm.dmPelsHeight;//物理高
//计算缩放
mt.Scale = ((float)mt.Physical.Height / (float)mt.Rect.Height);
//显示器fps
mt.FPS = (float)dm.dmDisplayFrequency;
}
size_t GetMonitor(std::list<MonitorInfo>* outMonitorInfo)
{
outMonitorInfo->clear();
//// 枚举显示器回调函数
::EnumDisplayMonitors(NULL, NULL, [](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) ->BOOL {
// 获取当前所有显示器的信息
std::list<MonitorInfo>* monitors = (std::list<MonitorInfo>*)dwData;
MonitorInfo mt;
GetMonitorInfo(mt, hMonitor);
monitors->push_back(mt);
return TRUE;
}, LPARAM(outMonitorInfo));
return outMonitorInfo->size();
}
void GetMontior(MonitorInfo* monitorInfo, HWND hWnd)
{
POINT cursorPos;
HMONITOR hMonitor;
if (hWnd) {
hMonitor = ::MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
}
else {
// 确定包含鼠标位置的屏幕
GetCursorPos(&cursorPos);
hMonitor = ::MonitorFromPoint(cursorPos, MONITOR_DEFAULTTONEAREST);
}
// 获取屏幕信息
GetMonitorInfo(*monitorInfo, hMonitor);
}
void GetMontior(MonitorInfo* monitorInfo, const Rect& rect)
{
RECT r = rect.ToRECT();
HMONITOR hMonitor = ::MonitorFromRect(&r, MONITOR_DEFAULTTONEAREST);
// 获取屏幕信息
GetMonitorInfo(*monitorInfo, hMonitor);
}
HCURSOR LoadCursor(Cursor cursorType)
{
return ::LoadCursorW(NULL, (LPTSTR)cursorType);
}
HCURSOR LoadCursor(const UIString& fileName)
{
return ::LoadCursorFromFileW(fileName.unicode().c_str());
}
void FreeCursor(HCURSOR hCursor)
{
::DestroyCursor(hCursor);
}
void DefaultNotify(Control* sender, EventArgs& args) {
WindowData* winData = NULL;
if (!sender || !(winData = sender->GetPublicData())) {
return;
}
auto* win = winData->Window;
switch (args.EventType)
{
case Event::OnMouseDoubleClick: {
if (sender->Action == ControlAction::Title) {
if (::IsZoomed(win->Hwnd())) {
win->ShowNormal();
}
else {
win->ShowMaximized();
}
}
break;
}
case Event::OnMouseDown: {
if (sender->Action == ControlAction::MoveWindow) {
winData->MoveWindow();
break;
}
if (sender->Action == ControlAction::Title) {
winData->TitleMoveWindow();
break;
}
if (sender->Action == ControlAction::Mini) {
win->ShowMinimized();
break;
}
if (sender->Action == ControlAction::Max) {
if (!win->IsMaximized()) {
win->ShowMaximized();
}
else {
win->ShowNormal();
}
break;
}
if (sender->Action == ControlAction::Close) {
win->Close();
break;
}
UIString tabName = sender->GetAttribute("tablayout");
if (!tabName.empty()) {
auto ctls = sender->Parent->FindControl("tablayout", tabName);
IFrame* frame = sender->GetFrame();
TabLayout* tabLayout = dynamic_cast<TabLayout*>(frame ? frame->FindControl(tabName) : win->FindControl(tabName));
if (tabLayout && sender->Parent) {
int pos = 0;
for (auto& it : ctls)
{
if (it == sender) {
tabLayout->SlideToPage(pos);
tabLayout->Invalidate();
break;
}
++pos;
}
}
}
break;
}
default:
break;
}
}
void ControlStyle::Scale(float scale)
{
this->FontSize = this->FontSize * scale + 0.5;
this->Border.Scale(scale);
if (this->BackImage) {
this->BackImage->DrawPosition.Scale(scale);
this->BackImage->DrawSize.Scale(scale);
}
if (this->ForeImage) {
this->ForeImage->DrawPosition.Scale(scale);
this->ForeImage->DrawSize.Scale(scale);
}
}
Object::Object(Object* parentObject)
{
if (parentObject) {
parentObject->Attach(this);
}
}
Object::~Object() {
}
void Object::SetAttribute(const UIString& attrName, const UIString& attrValue) {
auto itor = m_attrs.find(attrName);
if (itor != m_attrs.end()) {
(*itor).second = attrValue;
}
else {
m_attrs.insert(std::pair<UIString, UIString>(attrName, attrValue));
}
}
UIString Object::GetAttribute(const UIString& attrName) {
auto itor = m_attrs.find(attrName);
if (itor != m_attrs.end()) {
return (*itor).second;
}
return "";
}
const std::map<UIString, UIString>& Object::GetAttributes() {
return m_attrs;
}
void Object::RemoveAttribute(const UIString& attrName)
{
auto itor = m_attrs.find(attrName);
if (itor != m_attrs.end()) {
m_attrs.erase(itor);
}
}
Object* Object::Attach(Object* obj)
{
m_childObjects.Add(obj);
return obj;
}
void Object::Detach(Object* obj)
{
m_childObjects.Remove(obj);
}
void Object::DeleteLater()
{
BeginInvoke([this]() {
delete this;
});
}
void PaintEventArgs::PushLayer(const Geometry& dxGeometry) {
this->Graphics.PushLayer(dxGeometry);
m_layers.push_back(false);
}
void PaintEventArgs::PushLayer(const Rect& rectBounds) {
this->Graphics.PushAxisAlignedClip(rectBounds);
m_layers.push_back(true);
}
void PaintEventArgs::PopLayer() {
if (m_layers.size() > 0) {
if (*m_layers.rbegin() == true) {
this->Graphics.PopAxisAlignedClip();
}
else {
this->Graphics.PopLayer();
}
m_layers.pop_back();
}
}
void PaintEventArgs::PushOffset(const Point& offset)
{
this->Graphics.SetTransform(offset.X, offset.Y);
this->m_offsets.push_back(offset);
}
void PaintEventArgs::PopOffset()
{
this->m_offsets.pop_back();
if (this->m_offsets.size() > 0) {
auto& offset = *this->m_offsets.rbegin();
this->Graphics.SetTransform(offset.X, offset.Y);
}
}
};

90
sources/HLayout.cpp Normal file
View File

@@ -0,0 +1,90 @@
#include "HLayout.h"
namespace ezui {
void HLayout::SetAttribute(const UIString& key, const UIString& value)
{
if (key == "valign" || key == "align") {
if (value == "top") {
ContentAlign = VAlign::Top;
}
else if (value == "bottom") {
ContentAlign = VAlign::Bottom;
}
}
__super::SetAttribute(key, value);
}
void HLayout::OnLayout()
{
int contentHeight = 0;
int fixedWidth = 0;
int fixedTotal = 0;
int count = 0;//可见控件总数
for (auto& it : GetControls()) {
if ((it->IsAutoWidth() || it->IsAutoHeight()) && it->IsPendLayout()) {
it->RefreshLayout();
}
if (it->IsVisible() == false || (it->IsAutoWidth() && it->GetFixedWidth() <= 0)) continue;
++count;
auto width = it->GetFixedWidth();
if (width > 0) {
fixedWidth += width;
++fixedTotal;
}
fixedWidth += +it->Margin.GetHSpace();
}
int autoTotal = count - fixedTotal;
if (autoTotal == 0) {
//return;
}
double otherWidth = Width() * 1.0 - fixedWidth;
double autoWidth = otherWidth / autoTotal;
double maxRight = 0;
//排序
for (auto& it : GetControls()) {
if (it->IsVisible() == false) continue;
maxRight += it->Margin.Left;
int height = it->GetFixedHeight();
int y = it->Y();
if (height == 0) {
//当控件未指定绝对高度的时候
height = this->Height() - it->Margin.GetVSpace();
y = it->Margin.Top;
}
else {
//当控件指定了绝对高度的时候
if (ContentAlign == VAlign::Top) {
y = it->Margin.Top;
}
else if (ContentAlign == VAlign::Mid) {
y = int((this->Height() * 1.0 - height) / 2.0f + 0.5);
}
else if (ContentAlign == VAlign::Bottom) {
y = this->Height() - it->Margin.Bottom - height;
}
}
if (it->GetFixedWidth() > 0 || it->IsAutoWidth()) {
it->SetRect({ (int)maxRight ,y,it->GetFixedWidth() ,height });
maxRight += it->Width();
}
else {
it->SetRect({ (int)maxRight ,y,(int)autoWidth,height });
maxRight += it->Width();
}
maxRight += it->Margin.Right;
//统计上下文宽高
int maxBottom = it->GetRect().GetBottom() + it->Margin.Bottom;
if (maxBottom > contentHeight) {
contentHeight = maxBottom;
}
this->SetContentSize({ (int)(maxRight + 0.5) ,contentHeight });
}
}
HLayout::HLayout(Object* parentObject) :Control(parentObject)
{
}
HLayout::~HLayout()
{
}
};

92
sources/HListView.cpp Normal file
View File

@@ -0,0 +1,92 @@
#include "HListView.h"
namespace ezui {
void HListView::Init()
{
this->GetScrollBar()->SetWidth(Width());//滚动条宽度
this->GetScrollBar()->Parent = this;
this->GetScrollBar()->OffsetCallback = [this](int offsetValue)->void {
if (this->GetScrollBar()->ScrollPos() >= 1) {
NextPage();
}
this->Offset(offsetValue);
};
}
HListView::HListView(Object* parentObject) :PagedListView(parentObject)
{
Init();
}
HListView::~HListView()
{
}
void HListView::OnLayout() {
this->Offset(0);
if (IsAutoWidth()) {
this->GetScrollBar()->SetVisible(false);
}
else if (this->GetScrollBar()->IsVisible() == true) {
this->GetScrollBar()->SetVisible(true);
}
this->GetScrollBar()->RefreshScroll();
}
ScrollBar* HListView::GetScrollBar()
{
return &m_hScrollBar;
}
void HListView::Offset(int offset) {
int _contentHeight = 0;
int _contentWidth = offset;
for (auto& it : GetControls()) {
if (it->IsVisible() == false) {
it->SetX(0);
continue;
}
//处理y坐标和margin
{
int height = it->GetFixedHeight();
if (height == 0) {
height = this->Height() - it->Margin.GetVSpace();
}
int y = it->Y();
if (y == 0) {
y = it->Margin.Top;
}
if (y == 0 && height < this->Height()) {
y = int((this->Height() * 1.0 - height) / 2 + 0.5);
}
_contentWidth += it->Margin.Left;
it->SetRect(Rect(_contentWidth, y, it->Width(), height));
_contentWidth += it->Width();
_contentWidth += it->Margin.Right;
}
//计算最大高度
int _height = it->Y() + it->Height() + it->Margin.Bottom;
if (_height > _contentHeight) {
_contentHeight = _height;
}
}
this->SetContentSize({ _contentWidth - offset,_contentHeight });
}
void HListView::OnChildPaint(PaintEventArgs& args) {
ViewControls.clear();
//绘制子控件
auto rect = Rect(0, 0, Width(), Height());
for (auto& it : GetControls()) {
if (rect.IntersectsWith(it->GetRect())) {
ViewControls.push_back(it);
}
if (it->X() >= Width()) {
break;
////当控件超出容器底部将不再派发绘制事件 但是仍然要进行布局
//if (it->IsAutoWidth() && it->GetLayoutState() == LayoutState::Pend) {
// it->RefreshLayout();
//}
}
else {
it->SendEvent(args);
}
}
}
};

70
sources/HScrollBar.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include "HScrollBar.h"
namespace ezui {
HScrollBar::HScrollBar(Object* parentObj):ScrollBar(parentObj)
{
}
HScrollBar::~HScrollBar() {}
Rect HScrollBar::GetSliderRect() {
Rect sliderRect;
sliderRect.X = (int)m_sliderPos;
sliderRect.Y = 0;
sliderRect.Width = m_sliderLength;
sliderRect.Height = Height();
if (this->Scrollable() && sliderRect.Width <= 0) {
sliderRect.Width = 1;
}
return sliderRect;
}
void HScrollBar::ScrollTo(Control* ctl)
{
if (ctl && ctl->Parent && ctl->Parent == this->Parent) {
if (ctl->Parent->IsPendLayout()) {
ctl->Parent->RefreshLayout();
}
//控件的矩形位置
const Rect& ctlRect = ctl->GetRect();
if (ctlRect.X >= 0 && ctlRect.GetRight() <= Width()) {
return;
}
//出现在顶部
int offset = this->m_offset - ctlRect.X;
if (ctlRect.Y > 0) {
//出现在底部
offset += this->m_viewLength - ctlRect.Width;
}
__super::ScrollTo(offset, Event::None);
}
}
void HScrollBar::GetInfo(int* viewLength, int* contentLength, int* scrollBarLength)
{
*viewLength = this->Parent->Width();
*contentLength = this->Parent->GetContentSize().Width;
*scrollBarLength = Width();
}
void HScrollBar::ParentSize(const Size& size) {
this->SetRect({ 0,size.Height - this->Height(),size.Width,Height() });
}
void HScrollBar::OnMouseDown(const MouseEventArgs& arg) {
__super::OnMouseDown(arg);
Rect sliderRect = GetSliderRect();
if (sliderRect.IsEmptyArea()) { return; }
auto point = arg.Location;
if (arg.Button == MouseButton::Left && sliderRect.Contains({ point.X,point.Y })) {
m_mouseDown = true;
this->m_lastPoint = point.X;
}
}
void HScrollBar::OnMouseMove(const MouseEventArgs& arg)
{
__super::OnMouseMove(arg);
auto point = arg.Location;
if (m_mouseDown) {
int offsetX = point.X - this->m_lastPoint;
m_sliderPos += offsetX;
m_lastPoint = point.X;
int offset = m_sliderPos * this->m_rollRate + 0.5;
__super::ScrollTo(-offset, Event::OnMouseDrag);
}
}
};

56
sources/IFrame.cpp Normal file
View File

@@ -0,0 +1,56 @@
#include "IFrame.h"
namespace ezui {
IFrame::IFrame(Object* parentObject) :Control(parentObject)
{
}
IFrame::~IFrame()
{
}
UIManager* IFrame::GetUIManager()
{
return &m_umg;
}
void IFrame::SetAttribute(const UIString& attrName, const UIString& attrValue) {
if (attrName == "src") {
LoadXml(attrValue);
}
__super::SetAttribute(attrName, attrValue);
};
void IFrame::LoadXml(const UIString& fileName) {
m_umg.LoadXml(fileName);
m_umg.SetupUI(this);
}
void IFrame::LoadXml(const char* fileData, size_t fileSize) {
m_umg.LoadXml(fileData, fileSize);
m_umg.SetupUI(this);
}
Control* IFrame::Add(Control* childCtrl)
{
//IFrame下只允许有一个控件并且会随着IFrame拉伸
this->Clear();
if (childCtrl) {
auto* ctrl = __super::Add(childCtrl);
childCtrl->SetDockStyle(DockStyle::Fill);
return ctrl;
}
return NULL;
}
void IFrame::Remove(Control* childCtl, bool freeCtrl)
{
__super::Remove(childCtl, freeCtrl);
}
void IFrame::SetLayout(Control* ctrl) {
this->Add(ctrl);
}
Control* IFrame::GetLayout() {
return this->GetControl(0);
}
void IFrame::OnNotify(Control* sender, EventArgs& args) {
if (this->NotifyHandler) {
this->NotifyHandler(sender, args);
}
else {
ezui::DefaultNotify(sender, args);
}
}
}

161
sources/Label.cpp Normal file
View File

@@ -0,0 +1,161 @@
#include "Label.h"
namespace ezui {
Label::Label(Object* parentObject) :Control(parentObject)
{
}
Label::~Label() {}
void Label::OnDpiChange(const DpiChangeEventArgs& args) {
if (args.Scale != this->GetScale()) {
this->TextMargin.Scale(args.Scale / this->GetScale());
}
__super::OnDpiChange(args);
}
void Label::OnForePaint(PaintEventArgs& args)
{
__super::OnForePaint(args);
if (!m_wstr.empty()) {
int maxWidth = Width() - this->TextMargin.GetHSpace();
int maxHeight = Height() - this->TextMargin.GetVSpace();
std::wstring drawText(m_wstr);
std::wstring fontFamily = GetFontFamily();
auto fontSize = GetFontSize();
if (fontSize == 0)return;
Font font(fontFamily, fontSize);
args.Graphics.SetFont(font);
args.Graphics.SetColor(GetForeColor());
const std::wstring& wEllipsisText = m_ellipsisText;
if (!wEllipsisText.empty()) { //水平文本溢出的显示方案
Size ellipsisTextSize;
{
TextLayout textLayout(wEllipsisText, font);
ellipsisTextSize = textLayout.GetFontBox();
}
TextLayout textLayout(m_wstr, font);
if (textLayout.GetFontBox().Width > maxWidth) {//当文字显示超出的时候 宽度
int pos = 0;
BOOL isTrailingHit;
int fontHeight;
textLayout.HitTestPoint({ maxWidth,0 }, &pos, &isTrailingHit, &fontHeight);//对文字进行命中测试
drawText.erase(pos);
while (drawText.size() > 0)
{
//从最后往前删除文字 直到可以显示正常为止
drawText.erase(drawText.size() - 1, 1);
TextLayout textLayout(drawText, font);
if (textLayout.GetFontBox().Width + ellipsisTextSize.Width < maxWidth) {
drawText.append(wEllipsisText);
break;
}
}
}
}
std::wstring viewStr = !drawText.empty() ? drawText : wEllipsisText;
TextLayout textLayout(viewStr, font, SizeF(maxWidth, maxHeight), (IsAutoWidth() && IsAutoHeight()) ? TextAlign::TopLeft : this->TextAlign);
if (this->m_underlineCount != 0) {//下划线
textLayout.SetUnderline(m_underlinePos, m_underlineCount);
}
args.Graphics.DrawTextLayout(textLayout, { (float)this->TextMargin.Left,(float)this->TextMargin.Top });
}
}
void Label::SetAttribute(const UIString& key, const UIString& value) {
do
{
if (key == "valign" || key == "align") {
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" || key == "align") {
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 == "text") {
this->SetText(value);
break;
}
if (key == "underline") {
size_t pos = value.find(",");
this->m_underlinePos = std::atoi(value.substr(0, pos + 1).c_str());
this->m_underlineCount = std::atoi(value.substr(pos + 1, pos).c_str());
break;
}
if (key == "ellipsis") {
this->SetElidedText(value);
break;
}
} while (false);
__super::SetAttribute(key, value);
}
void Label::RefreshLayout()
{
//比较特殊需要屏蔽
this->OnLayout();
}
void Label::OnLayout() {
__super::OnLayout();
if (IsAutoWidth() || IsAutoHeight()) {
auto fontSize = GetFontSize();
if (fontSize == 0)return;
Font font(GetFontFamily(), fontSize);
int maxWidth = IsAutoWidth() ? EZUI_FLOAT_MAX : Width() - this->TextMargin.GetHSpace();
int maxHeight = IsAutoHeight() ? EZUI_FLOAT_MAX : Height() - this->TextMargin.GetVSpace();
TextLayout text(this->m_wstr, font, SizeF(maxWidth, maxHeight));
Size box = text.GetFontBox();
this->SetContentSize(box);
if (IsAutoWidth()) {
this->SetFixedWidth(box.Width + this->TextMargin.GetHSpace());
if (Parent) {
Parent->RefreshLayout();
}
}
if (IsAutoHeight()) {
this->SetFixedHeight(box.Height + this->TextMargin.GetVSpace());
if (Parent) {
Parent->RefreshLayout();
}
}
}
}
void Label::SetText(const UIString& text) {
m_wstr = text.unicode();
this->TryPendLayout();
}
void Label::SetUnderline(int pos, int count)
{
this->m_underlinePos = pos;
this->m_underlineCount = count;
}
UIString Label::GetText()const
{
return UIString(m_wstr);
}
void Label::SetElidedText(const UIString& text)
{
this->m_ellipsisText = text.unicode();
}
};

147
sources/LayeredWindow.cpp Normal file
View File

@@ -0,0 +1,147 @@
#include "LayeredWindow.h"
namespace ezui {
std::vector<LayeredWindow*> g_layeredWnds;
Timer g_layeredWndTimer;
//WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TRANSPARENT
LayeredWindow::LayeredWindow(int width, int height, HWND owner, DWORD dwStyle, DWORD dwExStyle) :BorderlessWindow(width, height, owner, dwStyle, dwExStyle | WS_EX_LAYERED)
{
//初始化全局绘制计时器
if (g_layeredWndTimer.Tick == NULL) {
g_layeredWndTimer.Interval = 5;
g_layeredWndTimer.Tick = [](Timer* t) {
t->Stop();
Invoke([]() {
for (auto& it : g_layeredWnds) {
it->Paint();
}
});
};
}
//添加到全局绘制队列
g_layeredWnds.push_back(this);
//获取公共数据
auto* publicData = this->GetPublicData();
publicData->InvalidateRect = [this](const Rect& rect) ->void {
//标记窗口无效区域
this->InvalidateRect(rect);
};
publicData->Refresh = [this]()->void {
//立即更新窗口中的无效区域
this->Paint();
};
//获取客户区大小 创建一个位图给窗口绘制
Size sz = GetClientRect().GetSize();
m_winBitmap = new Bitmap(sz.Width, sz.Height);
}
LayeredWindow::~LayeredWindow() {
if (m_winBitmap) {
delete m_winBitmap;
}
//从全局中移除
auto itor = std::find(g_layeredWnds.begin(), g_layeredWnds.end(), this);
if (itor != g_layeredWnds.end()) {
g_layeredWnds.erase(itor);
}
}
void LayeredWindow::InvalidateRect(const Rect& _rect) {
//将此区域添加到无效区域
m_invalidateRect.push_back(_rect);
//timer延迟绘制
g_layeredWndTimer.Start();
}
void LayeredWindow::BeginPaint(Rect* out_rect)
{
const Rect& clientRect = GetClientRect();
int Width = clientRect.Width;
int Height = clientRect.Height;
//将所有无效区域并集
for (auto& it : m_invalidateRect) {
Rect rect = it;
//这段代码是保证重绘区域一定是在窗口内
if (rect.X < 0) {
rect.Width += rect.X;
rect.X = 0;
if (rect.Width < 0) rect.Width = 0;
}
if (rect.Y < 0) {
rect.Height += rect.Y;
rect.Y = 0;
if (rect.Height < 0) rect.Height = 0;
}
if (rect.GetRight() > Width) {
rect.Width = Width - rect.X;
if (rect.Width < 0) rect.Width = 0;
}
if (rect.GetBottom() > Height) {
rect.Height = Height - rect.Y;
if (rect.Height < 0) rect.Height = 0;
}
//并集
Rect::Union(*out_rect, *out_rect, rect);
}
}
void LayeredWindow::EndPaint()
{
m_invalidateRect.clear();
}
void LayeredWindow::Paint()
{
if (IsVisible()) {
Rect invalidateRect;
BeginPaint(&invalidateRect);
if ((m_winBitmap && !invalidateRect.IsEmptyArea())) {
m_winBitmap->Earse(invalidateRect);//清除背景
HDC winHDC = m_winBitmap->GetDC();
#if 1
//不使用双缓冲
DoPaint(winHDC, invalidateRect);
#else
//使用双缓冲
Bitmap doubleBuff(m_winBitmap->Width(), m_winBitmap->Height());
DoPaint(doubleBuff.GetHDC(), invalidateRect);
//使用BitBlt函数进行复制到winHDC //如果窗体不规则 不适用于BitBlt进行复制
::BitBlt(winHDC, invalidateRect.X, invalidateRect.Y,
invalidateRect.Width, invalidateRect.Height,
doubleBuff.GetHDC(), invalidateRect.X, invalidateRect.Y,
SRCCOPY);
#endif
UpdateLayeredWindow(winHDC);//updatelaredwindow 更新窗口
EndPaint();
}
}
}
LRESULT LayeredWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
default:
break;
}
return __super::WndProc(uMsg, wParam, lParam);
}
void LayeredWindow::OnSize(const Size& sz) {
__super::OnSize(sz);
if (m_winBitmap) {
delete m_winBitmap;
}
m_winBitmap = new Bitmap(sz.Width, sz.Height);
Invalidate();
}
void LayeredWindow::UpdateLayeredWindow(HDC hdc) {
const Rect& _rectClient = GetClientRect();
POINT point{ _rectClient.X,_rectClient.Y };
SIZE size{ _rectClient.Width, _rectClient.Height };
BLENDFUNCTION blendFunc{ 0 };
blendFunc.SourceConstantAlpha = 255 * this->Opacity;
blendFunc.BlendOp = AC_SRC_OVER;
blendFunc.AlphaFormat = AC_SRC_ALPHA;
blendFunc.BlendFlags = 0;
::UpdateLayeredWindow(Hwnd(), NULL, NULL, &size, hdc, &point, 0, &blendFunc, ULW_ALPHA);//透明
}
}

54
sources/Menu.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include "Menu.h"
namespace ezui {
UINT_PTR g_nextId = WM_APP + 1;
inline UINT_PTR GenerateMenuId() {
++g_nextId;
return g_nextId;
}
Menu::Menu(Object* parentObj) : Object(parentObj)
{
m_hMenu = ::CreatePopupMenu(); // 创建空菜单
}
UINT_PTR Menu::Append(const UIString& text)
{
UINT_PTR id = GenerateMenuId();
if (m_hMenu) {
::AppendMenuW(m_hMenu, MF_STRING, id, text.unicode().c_str());
}
return id;
}
void Menu::Remove(UINT_PTR itemID)
{
if (!m_hMenu) return;
int count = ::GetMenuItemCount(m_hMenu);
for (int i = 0; i < count; ++i)
{
MENUITEMINFO mii = {};
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_ID;
if (::GetMenuItemInfo(m_hMenu, i, TRUE, &mii)) {
if (mii.wID == itemID) {
::RemoveMenu(m_hMenu, i, MF_BYPOSITION);
break;
}
}
}
}
Menu::~Menu()
{
if (m_hMenu) {
::DestroyMenu(m_hMenu);
}
}
HMENU Menu::HMenu()
{
return m_hMenu;
}
};

163
sources/NotifyIcon.cpp Normal file
View File

@@ -0,0 +1,163 @@
#include "NotifyIcon.h"
#define TRAY_ICON_MSG WM_APP+1 // 自定义托盘消息
namespace ezui {
bool g_bClassRegistered = false;
NotifyIcon::NotifyIcon(Object* parentObj) :Object(parentObj)
{
if (!g_bClassRegistered) {
::WNDCLASSEXW wcex = {};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = [](HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)->LRESULT {
WindowData* wndData = (WindowData*)UI_GET_USERDATA(hwnd);
if (wndData && wndData->WndProc) {
return wndData->WndProc(hwnd, message, wParam, lParam);
}
return ::DefWindowProc(hwnd, message, wParam, lParam);
};
wcex.hInstance = ezui::__EzUI__HINSTANCE;
wcex.lpszClassName = L"EzUI_NotifyIcon";
RegisterClassExW(&wcex);
g_bClassRegistered = true;
}
m_hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW, L"EzUI_NotifyIcon", L"", 0, 0, 0, 0, 0, NULL, NULL, ezui::__EzUI__HINSTANCE, NULL);
m_publicData.WndProc = [this](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)->LRESULT {
return this->WndProc(uMsg, wParam, lParam);
};
UI_SET_USERDATA(m_hWnd, (LONG_PTR)&m_publicData);
m_nid.cbSize = sizeof(m_nid);//结构体长度
m_nid.hWnd = m_hWnd;//窗口句柄
m_nid.uCallbackMessage = TRAY_ICON_MSG;//消息处理,这里很重要,处理鼠标点击
m_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
Shell_NotifyIconW(NIM_ADD, &m_nid);
}
LRESULT NotifyIcon::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg == TRAY_ICON_MSG) {
POINT point;
GetCursorPos(&point);
switch (LOWORD(lParam))
{
case WM_MOUSEMOVE: {
if (EventHandler) {
MouseEventArgs args(Event::OnMouseMove, { point.x, point.y }, MouseButton::None);
EventHandler(args);
}
break;
}
case WM_LBUTTONDOWN: {
if (EventHandler) {
MouseEventArgs args(Event::OnMouseDown, { point.x, point.y }, MouseButton::Left);
EventHandler(args);
}
break;
}
case WM_LBUTTONUP: {
if (EventHandler) {
MouseEventArgs args(Event::OnMouseUp, { point.x, point.y }, MouseButton::Left);
EventHandler(args);
}
break;
}
case WM_LBUTTONDBLCLK: {
if (EventHandler) {
MouseEventArgs args(Event::OnMouseDoubleClick, { point.x, point.y }, MouseButton::Left);
EventHandler(args);
}
break;
}
case WM_RBUTTONDOWN: {
if (this->EventHandler) {
MouseEventArgs args(Event::OnMouseDown, { point.x,point.y }, MouseButton::Right);
this->EventHandler(args);
}
if (m_menu) {
//如果设置了托盘菜单则弹出菜单
SetForegroundWindow(m_hWnd);
TrackPopupMenu(m_menu->HMenu(), TPM_RIGHTBUTTON, point.x, point.y, 0, m_hWnd, NULL);
}
break;
}
case WM_RBUTTONUP: {
if (EventHandler) {
MouseEventArgs args(Event::OnMouseUp, { point.x, point.y }, MouseButton::Right);
EventHandler(args);
}
break;
}
case WM_RBUTTONDBLCLK: {
if (EventHandler) {
MouseEventArgs args(Event::OnMouseDoubleClick, { point.x, point.y }, MouseButton::Right);
EventHandler(args);
}
break;
}
case WM_MBUTTONDOWN: {
if (EventHandler) {
MouseEventArgs args(Event::OnMouseDown, { point.x, point.y }, MouseButton::Middle);
EventHandler(args);
}
break;
}
case WM_MBUTTONUP: {
if (EventHandler) {
MouseEventArgs args(Event::OnMouseUp, { point.x, point.y }, MouseButton::Middle);
EventHandler(args);
}
break;
}
case WM_MBUTTONDBLCLK: {
if (EventHandler) {
MouseEventArgs args(Event::OnMouseDoubleClick, { point.x, point.y }, MouseButton::Middle);
EventHandler(args);
}
break;
}
default:
break;
}
}
else if (uMsg == WM_COMMAND) {
//菜单被点击
auto id = LOWORD(wParam);
if (m_menu && m_menu->MouseClick) {
m_menu->MouseClick(id);
}
}
return ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}
void NotifyIcon::SetIcon(HICON icon)
{
m_nid.hIcon = icon;
m_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
Shell_NotifyIconW(NIM_MODIFY, &m_nid);
}
void NotifyIcon::SetMenu(Menu* menu) {
this->m_menu = menu;
}
void NotifyIcon::SetTips(const UIString& text)
{
wcscpy_s(m_nid.szTip, text.unicode().c_str());
Shell_NotifyIconW(NIM_MODIFY, &m_nid);
}
void NotifyIcon::ShowBalloonTip(const UIString& title, const UIString& msg, int timeOut) {
m_nid.uTimeout = timeOut;
m_nid.uFlags = NIF_INFO;
m_nid.dwInfoFlags = NIIF_INFO;
wcscpy_s(m_nid.szInfoTitle, title.unicode().c_str());
wcscpy_s(m_nid.szInfo, msg.unicode().c_str());
Shell_NotifyIconW(NIM_MODIFY, &m_nid);
}
NotifyIcon::~NotifyIcon()
{
if (::IsWindow(m_hWnd)) {
Shell_NotifyIconW(NIM_DELETE, &m_nid);
::DestroyWindow(m_hWnd);
}
}
};

60
sources/PagedListView.cpp Normal file
View File

@@ -0,0 +1,60 @@
#include "PagedListView.h"
namespace ezui {
void PagedListView::SetPageInfo(const Controls& items, int pageSize)
{
this->m_pageIndex = 0;
this->m_pageSize = pageSize;
this->m_pageTotal = items.size() / pageSize + (items.size() % pageSize == 0 ? 0 : 1);
this->m_items = items;
this->NextPage();
}
void PagedListView::GetPage(int pageIndex, Controls* outCtls)
{
if (outCtls) {
outCtls->clear();
int beginIndex = (pageIndex - 1) * m_pageSize;
int count = 0;
for (int i = beginIndex; count < m_pageSize && i < m_items.size(); ++i)
{
outCtls->push_back(m_items[i]);
++count;
}
}
}
void PagedListView::Clear() {
__super::Clear();
}
void PagedListView::Clear(bool freeChilds) {
__super::Clear(freeChilds);
this->m_items.clear();
this->m_pageIndex = 0;
this->m_pageTotal = 0;
}
void PagedListView::NextPage()
{
if (m_items.size() <= 0)return;
if ((m_pageIndex + 1) > m_pageTotal)return;
if (NextPaging && NextPaging(this, (m_pageIndex + 1)) == false) {
return;
}
++m_pageIndex;
Controls ctls;
this->GetPage(m_pageIndex, &ctls);
for (auto& it : ctls) {
this->Add(it);
}
this->Invalidate();
}
PagedListView::PagedListView(Object* patentObject):Control(patentObject)
{
}
PagedListView::~PagedListView()
{
}
};

38
sources/PictureBox.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include "PictureBox.h"
namespace ezui {
PictureBox::PictureBox(Object* parentObject) :Control(parentObject)
{
Init();
}
void PictureBox::Init() {
m_timer = new Timer(this);
m_timer->Tick = [this](Timer* timer) {
timer->Stop();
HWND hWnd = this->Hwnd();
BeginInvoke([this, hWnd, timer]() {
if (!::IsWindow(hWnd))return;
if (this->Image) {
m_timer->Interval = this->Image->NextFrame();
this->Invalidate();
};
});
};
}
PictureBox::~PictureBox() {
}
void PictureBox::OnForePaint(PaintEventArgs& arg) {
if (Image) {
arg.Graphics.DrawImage(Image, RectF(0, 0, (float)Width(), (float)Height()));
if (Image->FrameCount() > 1) {
m_timer->Start();
}
}
__super::OnForePaint(arg);
}
void PictureBox::SetAttribute(const UIString& key, const UIString& value) {
__super::SetAttribute(key, value);
if (key == "src") {
Image = this->Attach(Image::Make(value));
}
}
};

72
sources/PopupWindow.cpp Normal file
View File

@@ -0,0 +1,72 @@
#include "PopupWindow.h"
namespace ezui {
PopupWindow::PopupWindow(int width, int height, HWND owner) :LayeredWindow(width, height, owner)
{
this->SetResizable(false);
}
PopupWindow::PopupWindow(int width, int height, Control* ownerCtl) :LayeredWindow(width, height, ownerCtl->Hwnd())
{
this->m_ownerCtl = ownerCtl;
this->SetResizable(false);
}
void PopupWindow::Show()
{
int x, y, width, height;
const Rect& rect = this->GetClientRect();
Rect ctlRect;
POINT location;
if (m_ownerCtl) {
ctlRect = m_ownerCtl->GetClientRect();
location.x = ctlRect.GetLeft();
location.y = ctlRect.GetBottom();
::ClientToScreen(m_ownerCtl->Hwnd(), &location);
}
else {
::GetCursorPos(&location);
}
MonitorInfo monitorInfo;
GetMontior(&monitorInfo, ::GetWindow(Hwnd(), GW_OWNER));
x = location.x;
y = location.y;
width = rect.Width;
height = rect.Height;
if (m_ownerCtl) {
width = ctlRect.Width;
}
if ((location.y + height) > monitorInfo.Rect.GetBottom()) {
y -= height;
if (m_ownerCtl) {
y -= ctlRect.Height;
}
}
//添加一点点偏移 不要紧贴着控件
if (y >= location.y) {
y += 1;
}
else {
y -= 1;
}
this->SetRect({ x, y, width, height });
__super::Show();
::SetForegroundWindow(Hwnd());
this->SetTopMost(true);
}
int PopupWindow::ShowModal(bool disableOnwer)
{
return __super::ShowModal(disableOnwer);
}
PopupWindow::~PopupWindow()
{
}
void PopupWindow::OnKillFocus(HWND hWnd)
{
::DestroyWindow(Hwnd());
}
};

118
sources/ProgressBar.cpp Normal file
View File

@@ -0,0 +1,118 @@
#include "ProgressBar.h"
namespace ezui {
ProgressBar::ProgressBar(Object* parentObject) : Control(parentObject) {
// 设置默认大小
SetFixedSize(Size(200, 25));
}
ProgressBar::~ProgressBar() {
}
void ProgressBar::SetProgress(float progress) {
// 限制进度值在 0.0f - 1.0f 范围内
float newProgress = (((0.0f) > ((((1.0f) < (progress)) ? (1.0f) : (progress)))) ? (0.0f) : ((((1.0f) < (progress)) ? (1.0f) : (progress))));
if (m_progress != newProgress) {
m_progress = newProgress;
Invalidate(); // 触发重绘
}
}
float ProgressBar::GetProgress() const {
return m_progress;
}
void ProgressBar::SetProgressColor(const Color& color) {
if (m_progressColor.GetValue() != color.GetValue()) {
m_progressColor = color;
Invalidate(); // 触发重绘
}
}
Color ProgressBar::GetProgressColor() const {
return m_progressColor;
}
void ProgressBar::SetBackgroundColor(const Color& color) {
if (m_backgroundColor.GetValue() != color.GetValue()) {
m_backgroundColor = color;
Invalidate(); // 触发重绘
}
}
Color ProgressBar::GetBackgroundColor() const {
return m_backgroundColor;
}
void ProgressBar::SetAttribute(const UIString& attrName, const UIString& attrValue) {
if (attrName == "progress") {
// 设置进度值,支持百分比和小数
float progress = 0.0f;
if (attrValue.find("%") != UIString::npos) {
// 百分比格式,如 "50%"
progress = std::atof(attrValue.substr(0, attrValue.length() - 1).c_str()) / 100.0f;
} else {
// 小数格式,如 "0.5"
progress = std::atof(attrValue.c_str());
}
SetProgress(progress);
} else if (attrName == "progress-color") {
// 设置进度条颜色
bool isGood = false;
Color color = Color::Make(attrValue, &isGood);
if (isGood) {
SetProgressColor(color);
}
} else {
// 调用基类方法处理其他属性
Control::SetAttribute(attrName, attrValue);
}
}
bool ProgressBar::ApplyStyleProperty(const UIString& key, const UIString& value) {
if (key == "progress") {
// CSS 样式中的进度设置
SetAttribute("progress", value);
return true;
} else if (key == "progress-color") {
// CSS 样式中的进度条颜色设置
SetAttribute("progress-color", value);
return true;
} else if (key == "background-color") {
// 背景颜色
bool isGood = false;
Color color = Color::Make(value, &isGood);
if (isGood) {
SetBackgroundColor(color);
return true;
}
}
// 调用基类方法处理其他样式
return Control::ApplyStyleProperty(key, value);
}
void ProgressBar::OnPaint(PaintEventArgs& args) {
// 获取控件矩形
RectF rect(0, 0, (float)Width(), (float)Height());
// 绘制背景
args.Graphics.SetColor(m_backgroundColor);
args.Graphics.FillRectangle(rect);
// 绘制进度
if (m_progress > 0.0f) {
args.Graphics.SetColor(m_progressColor);
float progressWidth = rect.Width * m_progress;
RectF progressRect(0, 0, progressWidth, rect.Height);
args.Graphics.FillRectangle(progressRect);
}
// 绘制边框(如果有设置)
Border border = GetStyle(State).Border;
if (border.Style != StrokeStyle::None && (border.Left > 0 || border.Top > 0 || border.Right > 0 || border.Bottom > 0)) {
OnBorderPaint(args, border);
}
}
}

23
sources/RadioButton.cpp Normal file
View File

@@ -0,0 +1,23 @@
#include "RadioButton.h"
namespace ezui {
RadioButton::RadioButton(Object* parentObject):CheckBox(parentObject)
{
}
void RadioButton::OnMouseDown(const MouseEventArgs& arg)
{
__super::OnMouseDown(arg);
SetCheck(true);
if (GetCheck() == true) {
for (auto& it : Parent->GetControls()) {
RadioButton* rbtn = dynamic_cast<RadioButton*>(it);
if (rbtn && rbtn != this && rbtn->GetCheck() == true) {
rbtn->SetCheck(false);
rbtn->Invalidate();
}
}
}
}
RadioButton::~RadioButton()
{
}
};

249
sources/Resource.cpp Normal file
View File

@@ -0,0 +1,249 @@
#include "Resource.h"
#include "EzUI.h"
namespace ezui {
//判断文件是否存在
bool DirectoryExists(const UIString& directoryNme) {
DWORD dwAttr = GetFileAttributesW(directoryNme.unicode().c_str());
if (dwAttr == DWORD(-1)) {
return false;
}
if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
{
return true;
}
return false;
}
//寻找指定目录以及目录下的所有文件
void FindFilesRecursively(const std::wstring& path, std::list<std::wstring>* result) {
WIN32_FIND_DATAW findData;
HANDLE findHandle = FindFirstFileW((path + L"/*").c_str(), &findData);
if (findHandle == INVALID_HANDLE_VALUE) {
return;
}
do
{
if (findData.cFileName[0] == L'.') {
continue;
}
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
// 如果是目录,递归查找该目录
std::wstring newPath = path + L"/" + findData.cFileName;
FindFilesRecursively(newPath, result);
}
else {
std::wstring newPath = path + L"/" + findData.cFileName;
result->push_back(newPath);
}
} while (FindNextFileW(findHandle, &findData));
FindClose(findHandle);
}
}
namespace ezui {
Resource::ReadStream::ReadStream(HRSRC hRsrc) {
this->m_ptr = (char*)::LoadResource(ezui::__EzUI__HINSTANCE, hRsrc);
this->m_count = ::SizeofResource(ezui::__EzUI__HINSTANCE, hRsrc);
}
Resource::ReadStream::ReadStream(const UIString& fileName) {
this->m_ifs = new std::ifstream(fileName.unicode(), std::ios::binary);
this->m_ifs->seekg(0, std::ios::end);
this->m_count = m_ifs->tellg();
this->m_ifs->seekg(0);
}
void Resource::ReadStream::seekg(std::streampos pos) {
if (m_ifs) {
m_ifs->seekg(pos);
}
else {
this->m_pos = pos;
}
}
void Resource::ReadStream::read(char* buf, std::streamsize count) {
if (m_ifs) {
m_ifs->read(buf, count);
}
else {
::memcpy(buf, m_ptr + m_pos, count);
this->m_pos += count;
}
}
std::streampos Resource::ReadStream::tellg() {
if (m_ifs) {
return m_ifs->tellg();
}
else {
return m_pos;
}
}
const std::streamsize Resource::ReadStream::size() {
return this->m_count;
}
Resource::ReadStream::~ReadStream() {
if (m_ifs) {
delete m_ifs;
}
if (m_ptr) {
::FreeResource((HGLOBAL)m_ptr);
}
}
bool Resource::Package(const UIString& dir, const UIString& outFile, const std::function<void(const UIString&, int, int)>& packCallback) {
if (dir.empty() || !DirectoryExists(dir)) {
std::wstring err = UIString("Error: Invalid directory: \"%s\"\n").format(dir.c_str()).unicode();
OutputDebugStringW(err.c_str());
ASSERT(!"Invalid directory !");
return false;
}
std::list<Entry> items;
std::list<std::wstring> files;
FindFilesRecursively(dir.unicode(), &files);
std::streamsize headOffset = sizeof(std::streamsize);
std::ofstream ofs(outFile.unicode(), std::ios::binary);
ofs.seekp(headOffset);
for (auto& file : files) {
//读取文件大小
std::ifstream ifs(file, std::ios::binary);
ifs.seekg(0, std::ios::end);
auto size = ifs.tellg();
//读取文件
UIString data;
data.resize(size);
ifs.seekg(0);
ifs.read((char*)data.c_str(), size);
//记录文件名称,偏移,大小
Entry item;
item.Name = file;
item.Size = size;
item.Offset = headOffset;
ofs.write(data.c_str(), data.size());
headOffset += data.size();
items.push_back(item);
}
//将偏移文件头偏移信息写入到文件初始位置
ofs.seekp(0);
ofs.write((char*)(&headOffset), sizeof(headOffset));
//设置到文件条目位置
ofs.seekp(headOffset);
//处理路径
UIString root = dir + "/";
root = root.replace("\\", "/");
root = root.replace("//", "/");
if (root[root.size() - 1] == '/') {
root.erase(root.size() - 1, 1);
}
size_t pos = root.rfind("/");
if (pos != std::string::npos) {
root = root.substr(0, pos + 1);
}
int index = 0;
for (auto& item : items) {
//写入文件偏移位置
ofs.write((char*)(&item.Offset), sizeof(headOffset));
//写入文件大小
ofs.write((char*)(&item.Size), sizeof(headOffset));
//文件路径名称
UIString name = item.Name;
name = name.replace("\\", "/");
name = name.replace("//", "/");
name = name.replace(root, "", false);
ofs.write(name.c_str(), name.size() + 1);
if (packCallback) {
packCallback(name, index, items.size());
}
index++;
}
//首位呼应
ofs.write((char*)(&headOffset), sizeof(headOffset));
ofs.flush();
ofs.close();
return ofs.good();
}
void Resource::UnPackage() {
m_isGood = false;
std::list<Entry>& items = (std::list<Entry>&)this->Items;
auto& ifs = *(this->m_rStream);
if (ifs.size() < sizeof(std::streamsize) * 2) {
//不是标准的资源文件 不执行解析
ASSERT(!"error resource");
return;
}
//读取记录文件位置的偏移
ifs.seekg(0);
std::streamsize headOffset;
ifs.read((char*)&headOffset, sizeof(headOffset));
//读取末尾
ifs.seekg(ifs.size() - sizeof(headOffset));
std::streamsize endValue;
ifs.read((char*)&endValue, sizeof(headOffset));
if (headOffset != endValue) {
//不是标准的资源文件 不执行解析
ASSERT(!"error resource");
return;
}
m_isGood = true;
//开始读取文件剩余条目
ifs.seekg(headOffset);
std::streampos endPos = ifs.size() - sizeof(headOffset);
while (ifs.tellg() < endPos)
{
//读取到文件偏移位置
std::streamsize fileOffset;
ifs.read((char*)&fileOffset, sizeof(headOffset));
//读取文件大小
std::streamsize fileSize;
ifs.read((char*)&fileSize, sizeof(headOffset));
//读取文件名称
std::vector<char> buf(32768);
for (int i = 0; i < buf.size(); i++)
{
char ch;
ifs.read((char*)&ch, 1);
buf[i] = ch;
if (ch == 0) {
break;
}
}
Entry item;
item.Offset = fileOffset;
item.Size = fileSize;
item.Name = buf.data();
items.push_back(item);
}
}
Resource::Resource(const UIString& resFile) {
this->m_rStream = new ReadStream(resFile);
this->UnPackage();
}
Resource::Resource(HRSRC hRsrc) {
this->m_rStream = new ReadStream(hRsrc);
this->UnPackage();
}
Resource:: ~Resource() {
if (m_rStream) {
delete m_rStream;
}
}
bool Resource::GetFile(const UIString& fileName, std::string* out) {
for (const auto& it : this->Items) {
if (it.Name == fileName) {
out->resize(it.Size);
m_rStream->seekg(it.Offset);
m_rStream->read((char*)out->c_str(), it.Size);
return true;
}
}
return false;
}
bool Resource::IsGood() {
return m_isGood;
}
void Resource::GetFile(const Entry& item, std::string* out) {
out->resize(item.Size);
m_rStream->seekg(item.Offset);
m_rStream->read((char*)out->c_str(), item.Size);
}
};

154
sources/ScrollBar.cpp Normal file
View File

@@ -0,0 +1,154 @@
#include "ScrollBar.h"
//滚动条相关
namespace ezui {
void ScrollBar::OnMouseUp(const MouseEventArgs& arg)
{
//::ReleaseCapture();
__super::OnMouseUp(arg);
m_mouseDown = false;
}
void ScrollBar::OnMouseLeave(const MouseEventArgs& arg)
{
__super::OnMouseLeave(arg);
m_mouseDown = false;
}
ScrollBar::ScrollBar(Object* parentObject) :Control(parentObject)
{
Init();
}
void ScrollBar::Init() {
Style.ForeColor = Color(205, 205, 205);//默认滑块颜色
SetSize({ 10,10 });
}
ScrollBar::~ScrollBar() {}
bool ScrollBar::IsDraw()
{
if (!Scrollable()) {
return false;
}
return this->IsVisible();
}
void ScrollBar::Reset()
{
this->m_sliderPos = 0;
this->m_offset = 0;
}
bool ScrollBar::Scrollable()
{
if (this->m_overflowLength > 0) {
return true;
}
return false;
}
void ScrollBar::OnBackgroundPaint(PaintEventArgs& e) {
if (!Scrollable()) {
return;
}
__super::OnBackgroundPaint(e);
}
void ScrollBar::OnForePaint(PaintEventArgs& args)
{
if (!Scrollable()) {
return;
}
__super::OnForePaint(args);
//绘制滑块
RectF sliderRect = GetSliderRect();
Color color = GetForeColor();
if (color.GetValue() != 0) {
args.Graphics.SetColor(color);
args.Graphics.FillRectangle(sliderRect, GetBorderTopLeftRadius());
}
}
void ScrollBar::OnMouseDown(const MouseEventArgs& arg)
{
//::SetCapture(this->PublicData->HANDLE);
__super::OnMouseDown(arg);
}
void ScrollBar::ScrollTo(float scrollRate) {
if (Parent == NULL) return;
if (Parent->IsPendLayout()) {
Parent->RefreshLayout();
}
int offset = scrollRate * this->m_overflowLength;
ScrollTo(-offset, Event::None);
}
float ScrollBar::ScrollPos()
{
if (this->m_overflowLength <= 0) return 1.0f;
return std::abs(this->m_offset) * 1.0f / this->m_overflowLength;
}
void ScrollBar::ScrollTo(int offset, const Event& type) {
if (Parent == NULL) return;
if (Parent->IsPendLayout()) {
Parent->RefreshLayout();
}
//if (!Scrollable()) {
// return;
//}
int viewLength;
int contentLength;
int scrollBarLength;
this->GetInfo(&viewLength, &contentLength, &scrollBarLength);
if (offset > 0) {
//滚动条在顶部
this->m_offset = 0;
this->m_sliderPos = 0;
}
else if (std::abs(offset) >= this->m_overflowLength) {
//滚动条在底部
this->m_offset = -this->m_overflowLength;
this->m_sliderPos = scrollBarLength - this->m_sliderLength;
}
else {
//正常滚动
this->m_offset = offset;
this->m_sliderPos = -offset / this->m_rollRate;
}
//调用容器的滚动函数进行偏移
if (OffsetCallback) {
OffsetCallback(this->m_offset);
SyncInfo();
}
Parent->Invalidate();
//Parent->Refresh();//可以用Refresh,这样滚动的时候的时候显得丝滑
if (Scroll) {
Scroll(this, (double)this->m_offset / (-this->m_overflowLength), type);
}
}
void ScrollBar::SyncInfo()
{
if (Parent == NULL)return;
if (Parent->IsPendLayout()) {
Parent->RefreshLayout();
}
int scrollBarLength;
this->GetInfo(&this->m_viewLength, &this->m_contentLength, &scrollBarLength);
this->m_overflowLength = this->m_contentLength - this->m_viewLength;//超出容器的内容长度
if (m_overflowLength > 0) {
this->m_sliderLength = (double)this->m_viewLength / this->m_contentLength * scrollBarLength + 0.5;//滑块长度
double rollTotal = scrollBarLength - this->m_sliderLength;//当前滑块可用滑道的总距离
this->m_rollRate = (double)(m_contentLength - this->m_viewLength) / rollTotal;//滑块每次滚动一次的对应上下文内容的比率
}
else {
this->m_sliderLength = scrollBarLength;
this->m_sliderPos = 0;
this->m_offset = 0;
this->m_rollRate = 0;
this->m_overflowLength = 0;
}
}
void ScrollBar::RefreshScroll() {
SyncInfo();
ScrollTo(this->m_offset, Event::None);
};
void ScrollBar::OnMouseWheel(const MouseEventArgs& arg) {
__super::OnMouseWheel(arg);
this->m_offset += arg.ZDelta * 0.5;
ScrollTo(this->m_offset, Event::OnMouseWheel);
}
};

176
sources/ShadowBox.cpp Normal file
View File

@@ -0,0 +1,176 @@
#include "ShadowBox.h"
namespace ezui {
ShadowBox::ShadowBox(int width, int height, HWND mainHwnd)
{
m_mainHWnd = mainHwnd;
DWORD dwFlags = WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT;
m_hWnd = CreateWindowExW(dwFlags, EZUI_WINDOW_CLASS, L"EzUI_ShadowWindow", WS_POPUP, 0, 0, width, height, NULL, NULL, ezui::__EzUI__HINSTANCE, NULL);
ASSERT(m_hWnd);
this->m_publicData = new WindowData;
//绑定消息过程
m_publicData->WndProc = [this](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ->LRESULT {
return this->WndProc(uMsg, wParam, lParam);
};
UI_SET_USERDATA(Hwnd(), this->m_publicData);
}
LRESULT ShadowBox::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
break;
default:
break;
}
return ::DefWindowProcW(Hwnd(), uMsg, wParam, lParam);
}
bool ShadowBox::SetShadow(int m_Width, int m_Height, int iSize, float _radius) {
float radius = _radius;
if (radius == 0) {//判断窗口是不是有圆角
//如果没有圆角就让窗口阴影加点弧度显得更和谐一点
radius = iSize / 2.0f;//半径
}
int width = m_Width < m_Height ? m_Width : m_Height;
int max_size = width / 2.0f - radius;
if (max_size <= 0) {
radius = 0;
max_size = width / 2.0f;
}
iSize = (int)iSize < max_size ? iSize : max_size;
double piAngle = 3.1415926;
int iSizeB = 4 * iSize;
double fN = piAngle / iSize / 5.0f;//设置四条边外模糊度 值越小越黑
double lN = 1.0 / iSize;
int iAplpha = 0;
int Left = iSize + radius,
Top = iSize + radius,
Right = m_Width - iSize - radius,
Bottom = m_Height - iSize - radius;
//处理四边的阴影
int x = 0, y = 0;
for (size_t i = 0; i < iSize; ++i)
{
iAplpha = int(255 - cos(fN * (i)) * 255);
for (x = Left; x <= Right; ++x)
{
SetAplpha(x, i, iAplpha, _radius);
SetAplpha(x, m_Height - i - 1, iAplpha, _radius);
}
for (y = Top; y <= Bottom; ++y)
{
SetAplpha(i, y, iAplpha, _radius);
SetAplpha(m_Width - i - 1, y, iAplpha, _radius);
}
}
//处理R角的阴影
double fL = 0;
int iSizeR = iSize + radius;
for (int y = 0; y < iSizeR; ++y)
{
for (int x = y; x < iSizeR; ++x)
{
fL = sqrt((iSizeR - x) * (iSizeR - x) + (iSizeR - y) * (iSizeR - y));
if (fL <= radius) {
break;
}
if (fL > radius && fL < iSizeR) {
iAplpha = int(255 - cos(fN * (iSizeR - fL)) * 255);
}
else {
iAplpha = 0;
}
SetAplpha(x, y, iAplpha, _radius);
SetAplpha(y, x, iAplpha, _radius);
SetAplpha(m_Width - x - 1, y, iAplpha, _radius);
SetAplpha(m_Width - y - 1, x, iAplpha, _radius);
SetAplpha(x, m_Height - y - 1, iAplpha, _radius);
SetAplpha(y, m_Height - x - 1, iAplpha, _radius);
SetAplpha(m_Width - x - 1, m_Height - y - 1, iAplpha, _radius);
SetAplpha(m_Width - y - 1, m_Height - x - 1, iAplpha, _radius);
}
}
return true;
}
void ShadowBox::SetAplpha(int x, int y, BYTE a, float radius) {
DWORD* point = (DWORD*)m_bufBitmap->GetPixel() + (x + y * m_bufBitmap->Width());//起始地址+坐标偏移
((BYTE*)point)[3] = a;//修改A通道数值
////使用A通道数值进行预乘
//float opacity = (1.0f / 255.0f) * a;
//((BYTE*)point)[2] = 50 * opacity;//修改R通道数值
//((BYTE*)point)[1] = 50 * opacity;//修改G通道数值
//((BYTE*)point)[0] = 50 * opacity;//修改B通道数值
}
void ShadowBox::Update(int shadowMargin, int radius) {
if (!::IsWindowVisible(m_mainHWnd) || ::IsIconic(m_mainHWnd)) {
::ShowWindow(m_hWnd, SW_HIDE);
return;
}
HWND ownerHWnd = ::GetWindow(m_mainHWnd, GW_OWNER);
//解决拥有窗口情况下阴影显示问题
SetWindowLongPtr(Hwnd(), GWLP_HWNDPARENT, (LONG_PTR)(ownerHWnd ? ownerHWnd : NULL));
::ShowWindow(m_hWnd, SW_SHOW);
::SetWindowPos(m_hWnd, m_mainHWnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
Rect mainWinRect;//主窗口矩形
{
RECT winRect;
::GetWindowRect(m_mainHWnd, &winRect);
mainWinRect.X = winRect.left;
mainWinRect.Y = winRect.top;
mainWinRect.Width = winRect.right - winRect.left;
mainWinRect.Height = winRect.bottom - winRect.top;
}
Size mainWinSize = mainWinRect.GetSize();//主窗口大小
//阴影窗口所在的位置
Rect shadowRect(mainWinRect.X - shadowMargin, mainWinRect.Y - shadowMargin, mainWinRect.Width + shadowMargin * 2, mainWinRect.Height + shadowMargin * 2);
::MoveWindow(m_hWnd, shadowRect.X, shadowRect.Y, shadowRect.Width, shadowRect.Height, FALSE);
//只有在大小发生改变或者圆角改变的时候才回去重新生成新的窗口阴影贴上去
if (mainWinSize.Equals(m_lastSize) && m_radius == radius && m_lastShadowMargin == shadowMargin) {
return;
}
m_lastShadowMargin = shadowMargin;//记录最后一次窗口阴影大小
m_radius = radius;//记录最后一次窗口圆角值
m_lastSize = mainWinSize;//记录最后一次阴影大小
if (m_bufBitmap) {
delete m_bufBitmap;
m_bufBitmap = NULL;
}
//创建用于渲染阴影的位图
m_bufBitmap = new Bitmap(shadowRect.Width, shadowRect.Height);
SetShadow(shadowRect.Width, shadowRect.Height, shadowMargin, radius);//渲染阴影
POINT point{ 0,0 };
SIZE size{ shadowRect.Width, shadowRect.Height };
BLENDFUNCTION blend{ 0 };
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.AlphaFormat = AC_SRC_ALPHA;
blend.SourceConstantAlpha = 255;
::UpdateLayeredWindow(m_hWnd, NULL, NULL, &size, m_bufBitmap->GetDC(), &point, 0, &blend, ULW_ALPHA);//。。。。。。更新分层窗口
}
HWND ShadowBox::Hwnd()
{
return m_hWnd;
}
ShadowBox::~ShadowBox()
{
if (::IsWindow(Hwnd())) {
HWND hwnd = m_hWnd;//复制一份
m_hWnd = NULL;//置零
::DestroyWindow(hwnd);//销毁
}
if (m_bufBitmap) {
delete m_bufBitmap;
}
if (m_publicData) {
delete m_publicData;
}
}
};

19
sources/Spacer.cpp Normal file
View File

@@ -0,0 +1,19 @@
#include "Spacer.h"
#include "Control.h"
namespace ezui {
Spacer::Spacer() {
this->SetHitTestVisible(false);
};
Spacer::~Spacer() {};
VSpacer::~VSpacer() {};
VSpacer::VSpacer(int fixedHeight) {
SetFixedHeight(fixedHeight);
}
HSpacer::~HSpacer() {};
HSpacer::HSpacer(int fixedWidth) {
SetFixedWidth(fixedWidth);
}
};

175
sources/TabLayout.cpp Normal file
View File

@@ -0,0 +1,175 @@
#include "TabLayout.h"
namespace ezui {
TabLayout::TabLayout(Object* parentObject) :Control(parentObject)
{
Init();
}
void TabLayout::Init()
{
m_timer = new Timer(this);
m_timer->Tick = [this](Timer* sender) {
HWND hWnd = this->Hwnd();
BeginInvoke([this, hWnd, sender]() {
if (!::IsWindow(hWnd))return;
m_stepAcc += m_stepPerFrame;
int stepMove = m_stepAcc;
m_stepAcc -= stepMove;
m_nowOffset += stepMove;
// 检查是否到达终点
if ((m_offset > 0 && m_nowOffset >= m_offset) ||
(m_offset < 0 && m_nowOffset <= m_offset)) {
m_nowOffset = m_offset;
sender->Stop();
this->Sort(); // 对齐页面
this->Invalidate();
return;
}
for (size_t i = 0; i < GetControls().size(); ++i) {
Control* ctl = GetControls()[i];
if (m_dlideDirection == SlideDirection::Horizontal) {
ctl->SetRect(Rect(m_initial[i] - m_nowOffset, 0, Width(), Height()));
}
else {
ctl->SetRect(Rect(0, m_initial[i] - m_nowOffset, Width(), Height()));
}
}
this->Invalidate();
});
};
}
TabLayout::~TabLayout()
{
}
void TabLayout::Remove(Control* ctl, bool freeCtl)
{
size_t index = this->IndexOf(ctl);
__super::Remove(ctl, freeCtl);
int newIndex = this->GetControls().size() - 1;
newIndex = newIndex < 0 ? 0 : newIndex;
this->SetPageIndex(newIndex);
TryPendLayout();
}
void TabLayout::Sort()
{
int pos = 0;
for (auto itor = GetControls().begin(); itor != GetControls().end(); ++itor)
{
if (m_dlideDirection == SlideDirection::Horizontal) {
int x = Width() * (pos - m_pageIndex);//子控件索引-当前所在页索引
(*itor)->SetRect(Rect(x, 0, Width(), Height()));
}
else {
int y = Height() * (pos - m_pageIndex);//子控件索引-当前所在页索引
(*itor)->SetRect(Rect(0, y, Width(), Height()));
}
(*itor)->SetVisible(true);
++pos;
}
}
void TabLayout::SetAttribute(const UIString& key, const UIString& value) {
__super::SetAttribute(key, value);
if (key == "tabindex") {
this->SetPageIndex(std::atoi(value.c_str()));
}
}
void TabLayout::OnLayout()
{
Sort();
this->EndLayout();
}
Control* TabLayout::Add(Control* childCtl)
{
auto ret = __super::Add(childCtl);
this->TryPendLayout();
return ret;
}
void TabLayout::SetPageIndex(int index)
{
this->m_pageIndex = index;
this->TryPendLayout();
}
void TabLayout::SlideToPage(int index, SlideDirection dlideDirection, int durationMs, int fps)
{
//滑动方向
m_dlideDirection = dlideDirection;
Sort();//先直接归位
int offsetTotal = (index - m_pageIndex) * (m_dlideDirection == SlideDirection::Horizontal ? Width() : Height());
if (offsetTotal == 0) {
return;
}
// 记录初始坐标
int controlCount = GetControls().size();
m_initial.clear();
m_initial.resize(controlCount);
for (int i = 0; i < controlCount; ++i) {
if (m_dlideDirection == SlideDirection::Horizontal) {
m_initial[i] = GetControls()[i]->X();
}
else {
m_initial[i] = GetControls()[i]->Y();
}
}
m_nowOffset = 0;
m_offset = offsetTotal;
m_pageIndex = index;
int FRAME_INTERVAL_MS = 1000 / fps;
int totalFrames = durationMs / FRAME_INTERVAL_MS;
if (totalFrames <= 0) {
totalFrames = 1;
}
m_stepPerFrame = offsetTotal * 1.0f / totalFrames;
m_timer->Interval = FRAME_INTERVAL_MS;
m_timer->Start();
}
void TabLayout::SetPage(Control* ctl)
{
#ifdef _DEBUG
ASSERT(ctl->Parent == this);
#endif // _DEBUG
this->m_pageIndex = this->IndexOf(ctl);
this->TryPendLayout();
}
Control* TabLayout::GetPage() {
int pos = 0;
for (auto& it : GetControls()) {
if (pos == this->m_pageIndex) {
return it;
}
++pos;
}
return NULL;
}
int TabLayout::GetPageIndex()
{
return m_pageIndex;
}
void TabLayout::OnChildPaint(PaintEventArgs& args)
{
ViewControls.clear();
Control* currPage = this->GetPage();
if (currPage) {//仅绘制当前页
ViewControls.push_back(currPage);
currPage->SendEvent(args);
}
}
};

132
sources/Task.cpp Normal file
View File

@@ -0,0 +1,132 @@
#include "Task.h"
namespace ezui {
Mutex::Mutex() {
InitializeCriticalSection(&m_mtx); // 初始化互斥锁
m_bLocked = false;
}
Mutex::~Mutex() {
DeleteCriticalSection(&m_mtx); // 销毁互斥锁
}
void Mutex::Lock() {
EnterCriticalSection(&m_mtx);
m_bLocked = true;
}
void Mutex::UnLock() {
LeaveCriticalSection(&m_mtx);
m_bLocked = false;
}
ConditionVariable::ConditionVariable() {
m_codv = CreateEvent(NULL, FALSE, FALSE, NULL); // 创建事件对象
}
ConditionVariable::~ConditionVariable() {
if (m_codv != NULL) {
CloseHandle(m_codv); // 关闭句柄,释放资源
}
}
void ConditionVariable::Notify() {
SetEvent(m_codv); // 设置事件,唤醒等待的线程
}
void ConditionVariable::Wait(const std::function<bool()>& condition_cb) {
m_mtx.Lock();// 先加锁
// 如果没有条件回调的情况下
if (!condition_cb) {
m_mtx.UnLock(); // 必须先解锁再等待!
WaitForSingleObject(m_codv, INFINITE);
return; //事件已触发 无需再检查 等待信号之前已经解锁 所以无需解锁 直接return
}
// 如果有条件回调 循环检查
while (!condition_cb()) {
m_mtx.UnLock(); // 解锁后再等待,避免死锁
WaitForSingleObject(m_codv, INFINITE);
m_mtx.Lock(); // 重新加锁检查条件
}
m_mtx.UnLock(); // 最终解锁
}
void ConditionVariable::Lock() {
m_mtx.Lock();
}
void ConditionVariable::Unlock() {
m_mtx.UnLock();
}
}
namespace ezui {
void Task::Wait() {
if (!m_bJoin)
{
m_bJoin = true;
m_thread->join(); // 等待线程完成
}
}
bool Task::IsStopped() {
return m_finished;
}
void Task::DoWork(std::function<void()>* func) {
(*func)(); // 执行任务
delete func;
m_finished = true;
}
Task::~Task() {
Wait(); // 等待任务完成
delete m_thread; // 删除线程对象
}
TaskFactory::TaskFactory(int maxTaskCount) {
for (size_t i = 0; i < maxTaskCount; ++i)
{
m_tasks.push_back(new Task([this]() {
while (true)
{
std::function<void()> task;
{
std::unique_lock<std::mutex> autoLock(this->m_mtx);
this->m_codv.wait(autoLock, [this]()->bool {
return this->m_bStop || !this->m_funcs.empty();
});
if (m_funcs.empty()) {
this->m_codv2.notify_all();
}
if (this->m_bStop && m_funcs.empty()) {
break;
}
task = std::move(*m_funcs.begin());
m_funcs.pop_front();
}
task();
}
}));
}
}
void TaskFactory::WaitAll() {
m_codv.notify_all();
std::unique_lock<std::mutex> lock2(this->m_mtx2);
this->m_codv2.wait(lock2, [this]()->bool {
return m_funcs.empty();
});
}
TaskFactory::~TaskFactory() {
{
std::unique_lock<std::mutex> autoLock(m_mtx);
m_bStop = true;
}
WaitAll();
while (!m_tasks.empty())
{
for (auto itor = m_tasks.begin(); itor != m_tasks.end(); )
{
if ((*itor)->IsStopped()) {
(*itor)->Wait();
delete (*itor);
itor = m_tasks.erase(itor);
}
else {
++itor;
}
}
}
}
};

729
sources/TextBox.cpp Normal file
View File

@@ -0,0 +1,729 @@
#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::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);
}
// 中心偏移用于 hit test 保证在行内
float offsetY = m_textLayout->GetFontHeight() / 2.0f;
point1.Y += offsetY;
point2.Y += offsetY;
// 获取每行矩形
auto lineRects = m_textLayout->GetLineRects();
for (auto& lr : lineRects) {
// 首行
if (point1.Y >= lr.Y && point1.Y <= lr.Y + lr.Height) {
if (point2.Y <= lr.Y + lr.Height) {
// 同一行
float width = std::max(0.0f, float(point2.X - point1.X));
m_selectRects.push_back(Rect(point1.X, lr.Y, width, lr.Height));
}
else {
// 跨行首行 从 point1 到行末
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 (point2.Y >= lr.Y && point2.Y <= lr.Y + lr.Height) {
float width = std::max(0.0f, point2.X - lr.X);
m_selectRects.push_back(Rect(lr.X, lr.Y, width, lr.Height));
}
// 中间整行
else if (point1.Y < lr.Y && point2.Y > 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) {
DeleteRange();//先删除是否有选中的区域
if (m_textPos < 0)m_textPos = 0;
if (m_textPos > (int)m_text.size()) {
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;
}
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 : Width();
m_textLayout = new TextLayout(*drawText, *m_font, SizeF{ width,(float)Height() }, this->TextAlign);
m_fontBox = m_textLayout->GetFontBox();
if (m_fontBox.Width < this->Width()) {
m_scrollX = 0;
}
if (!bAlignLeft && m_fontBox.Width > this->Width()) {
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 > this->Width() && m_scrollX + m_fontBox.Width < this->Width()) {
m_scrollX = this->Width() - m_fontBox.Width;
}
}
else {//多行编辑框
m_font->Get()->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
m_textLayout = new TextLayout(*drawText, *m_font, SizeF{ (float)Width(),EZUI_FLOAT_MAX }, TextAlign::TopLeft);
m_fontBox = m_textLayout->GetFontBox();
}
if (drawText != &this->m_text) {
delete drawText;
}
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) {
//使光标一直在输入框内
int drawX = m_careRect.X + m_scrollX;
if (drawX < 0) {//光标在最左侧
m_scrollX -= drawX;
}
if (drawX > Width()) {//光标在最右侧
int ofssetX = (Width() - drawX);
m_scrollX += ofssetX;
}
}
}
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 textWidth = m_fontBox.Width;
if (arg.ZDelta > 0 && textWidth > Width()) {
m_scrollX += std::abs(arg.ZDelta) * 0.5;
if (m_scrollX > 0) {
m_scrollX = 0;
}
Invalidate();
}
else if (arg.ZDelta<0 && textWidth>Width()) {
m_scrollX -= std::abs(arg.ZDelta) * 0.5;
if (-m_scrollX + Width() > textWidth) {
m_scrollX = -(textWidth - Width());
}
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;
int _y = -m_scrollY;
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) {//单行
//当鼠标往左侧移动
int textWidth = m_fontBox.Width;
if (m_lastX > point.X) {
m_lastX = point.X;
if (textWidth > Width() && m_scrollX < 0 && point.X < 0) {
m_scrollX += 3;
Invalidate();
return;
}
}
//当鼠标往右侧移动
if (m_lastX < point.X) {
m_lastX = point.X;
if (textWidth > Width() && point.X > Width()) {
m_scrollX -= 3;
if (-m_scrollX + Width() > textWidth) {
m_scrollX = -(textWidth - Width());
}
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;//偏移
rect.Y += m_scrollY;
return rect;
}
void TextBox::Insert(const UIString& str)
{
InsertUnicode(str.unicode());
Analysis();//分析字符串
}
void TextBox::SetAttribute(const UIString& key, const UIString& value) {
__super::SetAttribute(key, value);
do
{
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);
e.Graphics.DrawString(m_placeholder, RectF(0, 0, (float)Width(), (float)Height()), 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;//偏移
rect.Y += m_scrollY;
e.Graphics.FillRectangle(rect);
}
}
}
if (m_textLayout) {
e.Graphics.SetColor(fontColor);
e.Graphics.DrawTextLayout(*m_textLayout, PointF{ (float)m_scrollX, (float)m_scrollY });
}
if (!m_careRect.IsEmptyArea() && m_focus) {
if (m_bCareShow) {
RectF rect(m_careRect.X, m_careRect.Y, 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);
}
}
}
}

100
sources/TileListView.cpp Normal file
View File

@@ -0,0 +1,100 @@
#include "TileListView.h"
namespace ezui {
void TileListView::Init()
{
this->GetScrollBar()->SetHeight(Height());
this->GetScrollBar()->Parent = this;
this->GetScrollBar()->OffsetCallback = [this](int offsetValue)->void {
if (this->GetScrollBar()->ScrollPos() >= 1) {
NextPage();
}
this->Offset(offsetValue);
};
}
TileListView::TileListView(Object* parentObject) :PagedListView(parentObject)
{
Init();
}
TileListView::~TileListView()
{
}
ScrollBar* TileListView::GetScrollBar()
{
return &m_vScrollBar;
}
void TileListView::OnChildPaint(PaintEventArgs& args)
{
ViewControls.clear();
//绘制子控件
auto rect = Rect(0, 0, Width(), Height());
for (auto& it : GetControls()) {
if (rect.IntersectsWith(it->GetRect())) {
ViewControls.push_back(it);
}
if (it->Y() >= Height()) {
break;
////当控件超出容器底部将不再派发绘制事件 但是仍然要进行布局
//if ((it->IsAutoWidth() || it->IsAutoHeight()) && it->GetLayoutState() == LayoutState::Pend) {
// it->RefreshLayout();
//}
}
else {
it->SendEvent(args);
}
}
}
void TileListView::OnLayout()
{
this->Offset(0);
if (m_autoHeight) {
this->SetFixedHeight(this->GetContentSize().Height);
this->GetScrollBar()->SetVisible(false);
}
else if (this->GetScrollBar()->IsVisible() == true) {
this->GetScrollBar()->SetVisible(true);
}
this->GetScrollBar()->RefreshScroll();
}
void TileListView::Offset(int offset) {
int _contentWidth = 0;
int _contentHeight = 0;
int maxWidth = this->Width();
int maxHeight = 0;//每行最高的那个
int x = 0;
int y = offset;
for (auto& _it : GetControls()) {
if (_it->IsVisible() == false) {
_it->SetLocation(Point{ 0,0 });
continue;
}
Control& it = *_it;
int itemWidth = it.Width() + it.Margin.GetHSpace();
if (x + itemWidth > maxWidth) {
//换行
x = 0;
y += maxHeight;
maxHeight = 0;
}
x += it.Margin.Left;//左边距
int newX = x;//设置X坐标
int newY = y + it.Margin.Top;//设置Y坐标+上边距
it.SetRect(Rect(newX, newY, it.Width(), it.Height()));
int itemHeight = it.Height() + it.Margin.GetVSpace();//当前控件垂直占用的空间
if (maxHeight < itemHeight) {
maxHeight = itemHeight;
}
x += (it.Width() + it.Margin.Right);//右边距
_contentHeight = y + maxHeight;
//计算最大宽度
int _width = it.X() + it.Width();
if (_width > _contentWidth) {
_contentWidth = _width;
}
}
this->SetContentSize({ _contentWidth,_contentHeight - offset });
}
};

77
sources/Timer.cpp Normal file
View File

@@ -0,0 +1,77 @@
#include "Timer.h"
namespace ezui {
Timer::Timer(Object* parentObject) :Object(parentObject)
{
}
bool Timer::IsStopped() {
return m_bPause.load();
}
void Timer::Start() {
if (m_task == NULL) {
m_event = CreateEvent(NULL, FALSE, FALSE, NULL); // 创建事件对象
m_bExit = false;
m_task = new Task([this]() {
//等待信号与条件的lambda函数
const auto wait = [this]() {
while (!(this->m_bExit || !(this->m_bPause.load()))) {
WaitForSingleObject(m_event, INFINITE);
}
};
while (true)
{
wait();//等待信号与条件
if (this->m_bExit) {
break;//退出循环
}
//当等待时间过长(超过100毫秒) 将等待时间拆分为片段
auto start = std::chrono::steady_clock::now();
if (this->Interval > 100) {
while (!(this->m_bExit)) {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
if (elapsed >= this->Interval) {
break;
}
DWORD remain = (DWORD)(this->Interval - elapsed);
Sleep(remain > 100 ? 100 : remain);
}
}
else {
//等待时间过小 直接sleep
Sleep(this->Interval);
}
if (this->m_bExit) {
break;//退出循环
}
if (this->Tick) {
this->Tick(this);
}
}
});
}
//通知计时器继续运行
m_bPause.store(false);
if (m_event) {
SetEvent(m_event); // 设置事件, 唤醒等待的线程
}
}
void Timer::Stop() {
//暂停计时器的运行
m_bPause.store(true);
}
Timer::~Timer() {
m_bExit = true;
if (m_event) {
SetEvent(m_event); // 设置事件, 唤醒等待的线程
}
if (m_task) {
delete m_task; //结束掉轮询任务
}
if (m_event) {
CloseHandle(m_event); // 关闭句柄, 释放资源
}
}
};

26
sources/TreeView.cpp Normal file
View File

@@ -0,0 +1,26 @@
#include "TreeView.h"
#include "Label.h"
namespace ezui {
TreeView::TreeView(Object* parentObj) :HListView(parentObj)
{
m_vList.SetAutoWidth(true);
m_vList.SetDockStyle(DockStyle::Vertical);
this->Add(&m_vList);
}
void TreeView::AddNode(const UIString& nodeName)
{
Label* label = new Label(this);
label->TextAlign = TextAlign::MiddleLeft;
label->SetText(nodeName);
label->SetAutoSize(true);
m_vList.Add(label);
}
TreeView::~TreeView()
{
}
}

376
sources/UIManager.cpp Normal file
View File

@@ -0,0 +1,376 @@
#include "UIManager.h"
#include "IFrame.h"
#include "tinyxml.h"
#include "tinystr.h"
namespace ezui {
inline UIString __Attribute(TiXmlElement* node, const char* szstr) {
auto str = node->Attribute(szstr);
if (str == NULL) return "";
return str;
}
std::map<UIString, std::function<Control* ()>> g_createFunc;
void RegisterControl(const UIString& ctrlName, const std::function<Control* ()>& create_cb) {
if (!ctrlName.empty() && create_cb) {
g_createFunc[ctrlName.toLower()] = create_cb;
}
}
void InitControls()
{
RegisterControl<Control>("control");
RegisterControl<Control>("layout");
RegisterControl<Control>("box");
RegisterControl<VListView>("vlist");
RegisterControl<VListView>("vlistview");
RegisterControl<HListView>("hlist");
RegisterControl<HListView>("hlistview");
RegisterControl<VLayout>("vlayout");
RegisterControl<VLayout>("vbox");
RegisterControl<HLayout>("hlayout");
RegisterControl<HLayout>("hbox");
RegisterControl<TileListView>("tilelist");
RegisterControl<TileListView>("tilelistview");
RegisterControl<TabLayout>("tablayout");
RegisterControl<Spacer>("spacer");
RegisterControl<VSpacer>("vspacer");
RegisterControl<HSpacer>("hspacer");
RegisterControl<Label>("label");
RegisterControl<Button>("button");
RegisterControl<RadioButton>("radiobutton");
RegisterControl<CheckBox>("checkbox");
RegisterControl<ComboBox>("combobox");
RegisterControl<ComboBox>("select");
RegisterControl<TextBox>("edit");
RegisterControl<TextBox>("textbox");
RegisterControl<TextBox>("input");
RegisterControl<PictureBox>("img");
RegisterControl<PictureBox>("pictureBox");
RegisterControl<ProgressBar>("progressbar");
RegisterControl<ProgressBar>("progress");
RegisterControl<IFrame>("iframe");
}
};
namespace ezui {
int MathStyle(Control* ctl, const UIString& selectorName, const std::list<UIManager::Style>& selectors)
{
int mathCount = 0;
for (auto& it : selectors) {
if (it.m_selectorName == selectorName) {
++mathCount;
auto& styleType = it.m_styleType;
auto& styleStr = it.m_styleStr;
ctl->SetStyleSheet(styleType, styleStr);
}
}
//返回匹配成功的个数
return mathCount;
}
void ApplyStyle(Control* ctl, const UIString& selectName, const std::list<UIManager::Style>& selectors)
{
MathStyle(ctl, selectName, selectors);
MathStyle(ctl, selectName + ":checked", selectors);
MathStyle(ctl, selectName + ":active", selectors);
MathStyle(ctl, selectName + ":hover", selectors);
MathStyle(ctl, selectName + ":disabled", selectors);
//是否有滚动条 有滚动条则应用滚动条样式
ScrollBar* scrollBar = ctl->GetScrollBar();
if (scrollBar) {
UIString scrollBarSelectName = UIString("%s::-webkit-scrollbar").format(selectName.c_str());
MathStyle(scrollBar, scrollBarSelectName, selectors);
MathStyle(scrollBar, scrollBarSelectName + ":active", selectors);
MathStyle(scrollBar, scrollBarSelectName + ":hover", selectors);
MathStyle(scrollBar, scrollBarSelectName + ":disabled", selectors);
}
}
void UIManager::ApplyStyle(Control* ctl, const std::list<UIManager::Style>& selectors, const UIString& tagName) {
{//加载样式 使用标签选择器
if (!tagName.empty()) {
UIString selectName = tagName;
ezui::ApplyStyle(ctl, selectName, selectors);
}
}
{//加载样式 使用属性选择器
for (auto& attr : ctl->GetAttributes()) {
UIString selectName = UIString("[%s=%s]").format(attr.first.c_str(), attr.second.c_str());
ezui::ApplyStyle(ctl, selectName, selectors);
}
}
{//加载样式 使用类选择器
UIString classStr = ctl->GetAttribute("class");
ui_text::Replace(&classStr, " ", ",");//去除所有空格转换为逗号
auto classes = classStr.split(",");//分割类选择器
for (const auto& className : classes) { //一个控件可能有多个类名
UIString selectName = UIString(".%s").format(className.c_str());
ezui::ApplyStyle(ctl, selectName, selectors);
}
}
//加载样式 使用ID/name选择器
if (!(ctl->Name.empty())) {
UIString selectName = UIString("#%s").format(ctl->Name.c_str());
ezui::ApplyStyle(ctl, selectName, selectors);
}
{//加载内联样式 优先级最高
UIString sytle_static = ctl->GetAttribute("style");//内联样式语法
if (!sytle_static.empty()) { //内联样式只允许描述静态效果
ctl->SetStyleSheet(ControlState::Static, sytle_static.trim());
}
}
}
void UIManager::AnalysisStyle(const UIString& styleStr, std::list<UIManager::Style>* out) {
UIString style = styleStr.trim();
while (true)
{
//处理css的注释
auto pos1 = style.find("/*");
auto pos2 = style.find("*/", pos1 + 2);
if (pos1 != std::string::npos && pos2 != std::string::npos) {
style.erase(pos1, pos2 - pos1 + 2);
}
else {
break;
}
}
//分离每个样式
std::list<UIString> strs;
while (true)
{
auto pos1 = style.find("}");
if (pos1 != std::string::npos) {
strs.push_back(style.substr(0, pos1 + 1));
style.erase(0, pos1 + 1);
}
else {
break;
}
}
//解析样式类型
for (auto& style : strs) {
size_t pos2 = style.find("}");
if (pos2 == -1)break;
size_t pos3 = style.find("{");
UIString name = style.substr(0, pos3);
size_t pos4 = name.find(":");
UIString style_type;
UIString str = style.substr(pos3 + 1, pos2 - pos3 - 1);
if (pos4 != std::string::npos) {
style_type = name.substr(pos4 + 1);
style_type = style_type.trim();
}
//考虑到多个选择器
auto names = name.split(",");
for (auto& name : names) {
//添加至集合
UIManager::Style selector;
selector.m_selectorName = name.trim();
selector.m_styleStr = str.trim();
if (style_type == "hover") {
selector.m_styleType = ControlState::Hover;
}
else if (style_type == "active") {
selector.m_styleType = ControlState::Active;
}
else if (style_type == "checked") {
selector.m_styleType = ControlState::Checked;
}
else {
selector.m_styleType = ControlState::Static;
}
out->push_back(selector);
}
}
}
Control* UIManager::BuildControl(void* _node) {
TiXmlElement* node = (TiXmlElement*)_node;
Control* ctl = NULL;
std::string tagName(node->ValueTStr().c_str());
ctl = this->OnBuildControl(tagName);
if (ctl == NULL) {
UIString text = UIString("unknow element \"%s\"").format(tagName.c_str());
::MessageBoxA(NULL, text.c_str(), "UIManager Erro", MB_OK);
ctl = new Control;
}
//设置控件属性
TiXmlAttribute* attr = node->FirstAttribute();
do
{
if (!attr)break;
UIString attrName = attr->Name();
UIString attrValue(attr->Value());
ctl->SetAttribute(attrName, attrValue);
} while ((attr = attr->Next()));
return ctl;
}
void UIManager::LoadControl(void* _node, Control* control) {
TiXmlElement* node = (TiXmlElement*)_node;
TiXmlElement* fristChild = NULL;
if ((fristChild = node->FirstChildElement()))//先寻找子控件
{
UIString tagName = fristChild->Value();
Control* ctl = BuildControl(fristChild);
this->RegisterControl(ctl, tagName);
LoadControl(fristChild, ctl);
control->Add(ctl);
TiXmlElement* nextChild = fristChild->NextSiblingElement();
while (nextChild)//然后寻找兄弟
{
UIString tagName = nextChild->Value();
Control* ctl2 = BuildControl(nextChild);
this->RegisterControl(ctl2, tagName);
LoadControl(nextChild, ctl2);
control->Add(ctl2);
nextChild = nextChild->NextSiblingElement();
}
}
}
void UIManager::RegisterControl(Control* ctl, const UIString& tagNamee)
{
//弹簧需要交给控件自行处理
if (dynamic_cast<Spacer*>(ctl) == NULL) {
this->m_controls.emplace_front(ctl, tagNamee);
}
}
Control* UIManager::OnBuildControl(const UIString& tagName_) {
UIString tagName = tagName_.toLower();
//优先匹配全局自定义控件
for (auto& it : g_createFunc) {
if (it.first == tagName) {
Control* ctrl = it.second();
if (ctrl) {
return ctrl;
}
}
}
return NULL;
}
UIManager::UIManager()
{
}
void UIManager::SetupUI(Window* window)
{
Control* root = GetRoot();
if (root && !root->GetSize().Empty()) {
window->SetSize(root->GetSize());
window->CenterToScreen();
}
window->SetLayout(root);
}
void UIManager::SetupUI(Control* parentCtl)
{
if (GetRoot()) {
parentCtl->Add(GetRoot());
GetRoot()->SetDockStyle(DockStyle::Fill);
}
}
void UIManager::LoadXml(const UIString& fileName) {
std::string data;
if (GetResource(fileName, &data)) {
LoadXml(data.c_str(), data.size());
}
}
void UIManager::LoadXml(const char* fileData, size_t fileSize)
{
if (!m_rootNode.empty()) {
ASSERT(!"XML cannot be loaded repeatedly!");
}
TiXmlDocument doc;
auto result = doc.Parse(fileData, NULL, TiXmlEncoding::TIXML_ENCODING_UTF8);
//doc.Parse
TiXmlElement* element = doc.FirstChildElement();//read frist element
std::list<TiXmlElement*> controlNodes;
//存储样式
UIString styleStr;
//先处理样式
do
{
if (element == NULL) break;
if (element->ValueTStr() == "style") {// if element is style
const char* szStr = element->GetText();
if (szStr) {
styleStr = szStr;
}
}
else { //if no style , must be Control
controlNodes.push_back(element);
}
} while ((element = element->NextSiblingElement()));
//然后处理控件
for (auto& element : controlNodes) {
UIString tagName = element->Value();
Control* control = BuildControl(element);//先加载根节点
this->RegisterControl(control, tagName);
m_rootNode.push_back(control);//存入根节点集合
LoadControl(element, control);//加载子节点
}
//加载样式
this->SetStyleSheet(styleStr);
}
void UIManager::LoadStyle(const UIString& fileName)
{
std::string data;
if (!GetResource(fileName, &data)) {
return;
}
this->SetStyleSheet(data);
}
void UIManager::SetStyleSheet(const UIString& styleContent)
{
UIString data = styleContent;
std::list<UIManager::Style> styles;
//分析出样式
this->AnalysisStyle(data, &styles);
//保存样式到全局
this->m_styles.splice(this->m_styles.end(), styles); // 把 styles 的所有元素移动到 this->_styles 末尾
if (!this->m_styles.empty()) {
for (auto& it : this->m_controls) {
//对所有加载进来的控件应用样式
this->ApplyStyle(it.m_ctl, this->m_styles, it.m_tagName);
}
}
}
void UIManager::Free(Control** ctl)
{
for (auto itor = m_controls.begin(); itor != m_controls.end(); ++itor)
{
if (itor->m_ctl == *ctl) {
delete* ctl;
*ctl = NULL;
m_controls.erase(itor);
break;
}
}
}
UIManager::~UIManager() {
for (auto& it : m_controls) {
delete it.m_ctl;
}
}
Control* UIManager::GetRoot(int index) {
if (index >= 0 && index < m_rootNode.size()) {
return m_rootNode[index];
}
return NULL;
}
};

91
sources/UISelector.cpp Normal file
View File

@@ -0,0 +1,91 @@
#include "UISelector.h"
namespace ezui {
UISelector::UISelector(const std::vector<Control*>& controls)
{
this->m_ctls = controls;
}
UISelector::UISelector(const std::list<Control*>& controls)
{
this->m_ctls.resize(controls.size());
for (auto& it : controls) {
this->m_ctls.push_back(it);
}
}
UISelector::UISelector(Control* ctl)
{
this->m_ctl = ctl;
}
UISelector::UISelector(Control* ct, const UIString& _mathStr)
{
UIString mathStr = _mathStr;
mathStr.replace(" ", " ");
//模仿jquery进行元素匹配
}
UISelector::~UISelector()
{
}
UISelector& UISelector::Css(const UIString& styleStr)
{
/* for (auto& it : this->ctls) {
if (notCtl == it)continue;
it->SetStyleSheet(styleStr);
}
if (ctl) {
if (notCtl == ctl)return *this;
ctl->SetStyleSheet(styleStr);
}*/
return *this;
}
UISelector& UISelector::CssHover(const UIString& styleStr)
{
/* for (auto& it : this->ctls) {
if (notCtl == it)continue;
it->SetStyleSheet(styleStr, ControlState::Hover);
}
if (ctl) {
if (notCtl == ctl)return *this;
ctl->SetStyleSheet(styleStr, ControlState::Hover);
}*/
return *this;
}
UISelector& UISelector::CssActive(const UIString& styleStr)
{
/* for (auto& it : this->ctls) {
if (notCtl == it)continue;
it->SetStyleSheet(styleStr, ControlState::Active);
}
if (ctl) {
if (notCtl == ctl)return *this;
ctl->SetStyleSheet(styleStr, ControlState::Active);
}*/
return *this;
}
UISelector& UISelector::Attr(const UIString& key, const UIString& value)
{
for (auto& it : this->m_ctls) {
if (m_notCtl == it)continue;
it->SetAttribute(key, value);
}
if (m_ctl) {
if (m_notCtl == m_ctl)return *this;
m_ctl->SetAttribute(key, value);
}
return *this;
}
UISelector& UISelector::Refresh()
{
for (auto& it : this->m_ctls) {
it->Invalidate();
}
if (m_ctl) {
m_ctl->Invalidate();
}
return *this;
}
UISelector& UISelector::Not(Control* fiterCtl) {
this->m_notCtl = fiterCtl;
return *this;
}
};

294
sources/UIString.cpp Normal file
View File

@@ -0,0 +1,294 @@
#include "UIString.h"
namespace ezui {
namespace ui_text {
//-----------------------------------------------Copy Start-----------------------------------------------
size_t String::utf8Length() const {
auto* p = this->c_str();
size_t pos = 0, count = 0;
while (p[pos] && pos < this->size()) {
if ((p[pos] & 0xc0) != 0x80) {
++count;
}
++pos;
}
return count;
}
String::String() {}
String::~String() {}
String::String(const String& _right)noexcept :std::string(_right) {}
String::String(String&& _right) noexcept :std::string(std::move(_right)) {}
String& String::operator=(const String& _right) noexcept
{
(std::string&)*this = _right;
return *this;
}
String& String::operator=(String&& _right) noexcept
{
std::string::operator=(std::move(_right));
return *this;
}
String::String(const std::string& str)noexcept :std::string(str) {}
String::String(const char* szbuf)noexcept :std::string(szbuf) {}
String::String(const wchar_t* szbuf)noexcept {
if (szbuf == NULL)return;
UnicodeToUTF8(szbuf, this);
}
String::String(const std::wstring& wstr)noexcept {
UnicodeToUTF8(wstr, this);
}
void String::erase(char _ch)
{
Erase(this, _ch);
}
void String::erase(size_t pos, size_t count)
{
__super::erase(pos, count);
}
String String::replace(char oldChar, char newChar) const
{
String newStr = *this;
Replace(&newStr, oldChar, newChar);
return newStr;
}
String String::replace(const String& oldText, const String& newText, bool allReplace) const
{
String newStr = *this;
Replace(&newStr, oldText, newText, allReplace);
return newStr;
}
String String::toLower() const
{
String str(*this);
Tolower(&str);
return str;
}
String String::toUpper() const
{
String str(*this);
Toupper(&str);
return str;
}
String String::trim()const {
String data = *this;
const char* raw = data.c_str();
size_t totalLen = data.size();
// 找起始位置
size_t start = 0;
while (start < totalLen && raw[start] == ' ') {
++start;
}
// 找结束位置(最后一个非空格字符之后的位置)
size_t end = totalLen;
while (end > start && raw[end - 1] == ' ') {
--end;
}
return std::string(raw + start, end - start);
}
size_t String::count(const String& value)const
{
size_t count = 0;
size_t pos = 0;
while ((pos = this->find(value, pos)) != std::string::npos)
{
++count;
pos += value.size();
}
return count;
}
bool String::operator==(const wchar_t* szbuf)const
{
std::string u8str;
UnicodeToUTF8(szbuf, &u8str);
return (*this == u8str);
}
bool String::operator==(const std::wstring& wStr)const
{
std::string u8str;
UnicodeToUTF8(wStr, &u8str);
return (*this == u8str);
}
std::wstring String::unicode() const {
std::wstring wstr;
UTF8ToUnicode(*this, &wstr);
return wstr;
}
std::string String::ansi() const {
std::string str;
UTF8ToANSI(*this, &str);
return str;
}
void AnyToUnicode(const std::string& src_str, UINT codePage, std::wstring* out_wstr) {
std::wstring& wstrCmd = *out_wstr;
auto bytes = ::MultiByteToWideChar(codePage, 0, src_str.c_str(), src_str.size(), NULL, 0);
wstrCmd.resize(bytes);
bytes = ::MultiByteToWideChar(codePage, 0, src_str.c_str(), src_str.size(), const_cast<wchar_t*>(wstrCmd.c_str()), wstrCmd.size());
}
void UnicodeToAny(const std::wstring& wstr, UINT codePage, std::string* out_str) {
std::string& strCmd = *out_str;
auto bytes = ::WideCharToMultiByte(codePage, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);
strCmd.resize(bytes);
bytes = ::WideCharToMultiByte(codePage, 0, wstr.c_str(), wstr.size(), const_cast<char*>(strCmd.c_str()), strCmd.size(), NULL, NULL);
}
//以下是静态函数
void ANSIToUniCode(const std::string& str, std::wstring* outStr)
{
AnyToUnicode(str, ::GetACP(), outStr);
}
void UnicodeToANSI(const std::wstring& wstr, std::string* outStr)
{
UnicodeToAny(wstr, ::GetACP(), outStr);
}
void GBKToUTF8(const std::string& str, std::string* outStr) {
UINT gbkCodePage = 936;
std::wstring wstr;
AnyToUnicode(str, gbkCodePage, &wstr);
UnicodeToUTF8(wstr, outStr);
}
void UTF8ToGBK(const std::string& str, std::string* outStr) {
UINT gbkCodePage = 936;
std::wstring wstr;
UTF8ToUnicode(str, &wstr);
UnicodeToAny(wstr, gbkCodePage, outStr);
}
void ANSIToUTF8(const std::string& str, std::string* outStr)
{
UINT codePage = ::GetACP();
if (codePage == CP_UTF8) {
*outStr = str;//如果本身就是utf8则不需要转换
return;
}
std::wstring wstr;
AnyToUnicode(str, codePage, &wstr);
UnicodeToUTF8(wstr, outStr);
}
void UTF8ToANSI(const std::string& str, std::string* outStr) {
UINT codePage = ::GetACP();
if (codePage == CP_UTF8) {
*outStr = str;//如果本身就是utf8则不需要转换
return;
}
std::wstring wstr;
UTF8ToUnicode(str, &wstr);
UnicodeToAny(wstr, codePage, outStr);
}
void UnicodeToUTF8(const std::wstring& wstr, std::string* outStr)
{
UnicodeToAny(wstr, CP_UTF8, outStr);
}
void UTF8ToUnicode(const std::string& str, std::wstring* outStr) {
AnyToUnicode(str, CP_UTF8, outStr);
}
void Tolower(std::string* str_in_out)
{
std::string& str = *str_in_out;
for (size_t i = 0; i < str.size(); ++i)
{
char& ch = (char&)str.c_str()[i];
if (ch >= 65 && ch <= 90) {
ch += 32;
}
}
}
void Toupper(std::string* str_in_out)
{
std::string& str = *str_in_out;
for (size_t i = 0; i < str.size(); ++i)
{
char& ch = (char&)str.c_str()[i];
if (ch >= 97 && ch <= 122) {
ch -= 32;
}
}
}
void Erase(std::string* str_in_out, char _char) {
const String& self = *str_in_out;
char* bufStr = new char[self.size() + 1] { 0 };
size_t pos = 0;
for (auto& it : self) {
if (_char == it)continue;
bufStr[pos] = it;
++pos;
}
*str_in_out = bufStr;
delete[] bufStr;
}
size_t Replace(std::string* str_in_out, const std::string& oldText, const std::string& newText, bool replaceAll)
{
if (!str_in_out || oldText.empty()) return 0;
size_t count = 0;
std::string& str = *str_in_out;
size_t pos = 0;
while ((pos = str.find(oldText, pos)) != std::string::npos)
{
str.replace(pos, oldText.size(), newText);
++count;
if (!replaceAll) {
break;
}
}
return count;
}
void Replace(std::string* str_in_out, char oldChar, char newChar)
{
for (auto& it : *str_in_out) {
if (it == oldChar) {
it = newChar;
}
}
}
template<typename T>
void __Split(const std::string& str_in, const std::string& ch_, std::vector<T>* strs_out) {
std::vector<T>& arr = *strs_out;
arr.clear();
if (str_in.empty()) return;
std::string buf = str_in;
size_t pos = buf.find(ch_);
if (pos == std::string::npos) {
arr.push_back(buf);
return;
}
while (pos != std::string::npos) {
auto item = buf.substr(0, pos);
if (!item.empty()) {
arr.push_back(item);
}
buf = buf.erase(0, pos + ch_.size());
pos = buf.find(ch_);
if (pos == std::string::npos) {
if (!buf.empty()) {
arr.push_back(buf);
}
}
}
}
std::vector<String> String::split(const String& ch)const
{
std::vector<String> strs;
__Split<String>(*this, ch, &strs);
return strs;
}
void Split(const std::string& str_in, const std::string& ch_, std::vector<std::string>* strs_out) {
__Split<std::string>(str_in, ch_, strs_out);
}
String ToString(double number, size_t keepBitSize) {
std::ostringstream oss;
oss << std::fixed << std::setprecision(keepBitSize) << number;
return oss.str();
}
//-----------------------------------------------Copy End-----------------------------------------------
};
};

92
sources/VLayout.cpp Normal file
View File

@@ -0,0 +1,92 @@
#include "VLayout.h"
namespace ezui {
VLayout::VLayout(Object* parentObject) :Control(parentObject)
{
}
void VLayout::SetAttribute(const UIString& key, const UIString& value)
{
if (key == "halign" || key == "align") {
if (value == "left") {
ContentAlign = HAlign::Left;
}
else if (value == "right") {
ContentAlign = HAlign::Right;
}
}
__super::SetAttribute(key, value);
}
VLayout::~VLayout()
{
}
void VLayout::OnLayout()
{
int contentWidth = 0;
int fixedHeight = 0;
int fixedTotal = 0;
int count = 0;//可见控件总数
for (auto& it : GetControls()) {
if ((it->IsAutoWidth() || it->IsAutoHeight()) && it->IsPendLayout()) {
it->RefreshLayout();
}
if (it->IsVisible() == false || (it->IsAutoHeight() && it->GetFixedHeight() <= 0))continue;
++count;
auto height = it->GetFixedHeight();
if (height > 0) {
fixedHeight += height;
++fixedTotal;
}
fixedHeight += +it->Margin.GetVSpace();
}
int autoTotal = count - fixedTotal;
if (autoTotal == 0) {
//return;
}
double otherHeight = Height() * 1.0 - fixedHeight;
double autoHeight = otherHeight / autoTotal;
double maxBottom = 0;
//排序
for (auto& it : GetControls()) {
if (it->IsVisible() == false)continue;
maxBottom += it->Margin.Top;
int width = it->GetFixedWidth();
int x = it->X();
if (width == 0) {
//当控件未指定绝对宽度的时候
width = this->Width() - it->Margin.GetHSpace();
x = it->Margin.Left;
}
else {
//当控件指定了绝对宽度的时候
if (ContentAlign == HAlign::Left) {
x = it->Margin.Left;
}
else if (ContentAlign == HAlign::Center) {
x = int((this->Width() * 1.0 - width) / 2.0f + 0.5);
}
else if (ContentAlign == HAlign::Right) {
x = this->Width() - it->Margin.Right - width;
}
}
if (it->GetFixedHeight() > 0 || it->IsAutoHeight()) {
it->SetRect({ x, (int)maxBottom,width, it->GetFixedHeight() });
maxBottom += it->Height();
}
else {
it->SetRect({ x ,(int)maxBottom,width ,(int)autoHeight });
maxBottom += it->Height();
}
maxBottom += it->Margin.Bottom;
//统计上下文宽高
int maxRight = it->GetRect().GetRight() + it->Margin.Right;
if (maxRight > contentWidth) {
contentWidth = maxRight;
}
this->SetContentSize({ contentWidth ,(int)(maxBottom + 0.5) });
}
}
};

94
sources/VListView.cpp Normal file
View File

@@ -0,0 +1,94 @@
#include "VListView.h"
namespace ezui {
void VListView::Init()
{
this->GetScrollBar()->SetHeight(Height());//滚动条宽度
this->GetScrollBar()->Parent = this;
this->GetScrollBar()->OffsetCallback = [this](int offsetValue)->void {
if (this->GetScrollBar()->ScrollPos() >= 1) {
NextPage();
}
this->Offset(offsetValue);
};
}
VListView::VListView(Object* parentObject) :PagedListView(parentObject)
{
Init();
}
VListView::~VListView()
{
}
void VListView::OnLayout() {
this->Offset(0);
if (IsAutoHeight()) {
this->GetScrollBar()->SetVisible(false);
}
else if (this->GetScrollBar()->IsVisible() == true) {
this->GetScrollBar()->SetVisible(true);
}
this->GetScrollBar()->RefreshScroll();
this->EndLayout();
}
ScrollBar* VListView::GetScrollBar()
{
return &m_vScrollBar;
}
void VListView::Offset(int offset)
{
int contentWidth = 0;
int _maxBottom = offset;
for (auto& it : GetControls()) {
if (it->IsVisible() == false) {
it->SetY(0);
continue;
}
{
//处理margin和x坐标
int width = it->GetFixedWidth();
if (width == 0) {
width = this->Width() - it->Margin.GetHSpace();
}
int x = it->X();
if (x == 0) {
x = it->Margin.Left;
}
if (x == 0 && width < this->Width()) {
x = int((this->Width() * 1.0 - width) / 2 + 0.5);
}
_maxBottom += it->Margin.Top;
it->SetRect(Rect{ x,_maxBottom,width,it->Height() });
_maxBottom += it->Height();
_maxBottom += it->Margin.Bottom;
}
//计算最大宽度
int _width = it->X() + it->Width() + it->Margin.Right;
if (_width > contentWidth) {
contentWidth = _width;
}
}
this->SetContentSize({ contentWidth, _maxBottom - offset });
}
void VListView::OnChildPaint(PaintEventArgs& args) {
ViewControls.clear();
//绘制子控件
auto rect = Rect(0, 0, Width(), Height());
for (auto& it : GetControls()) {
if (rect.IntersectsWith(it->GetRect())) {
ViewControls.push_back(it);
}
if (it->Y() >= Height()) {
break;
////当控件超出容器底部将不再派发绘制事件 但是仍然要进行布局
//if (it->IsAutoHeight() && it->GetLayoutState() == LayoutState::Pend) {
// it->RefreshLayout();
//}
}
else {
it->SendEvent(args);
}
}
}
};

70
sources/VScrollBar.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include "VScrollBar.h"
namespace ezui {
VScrollBar::VScrollBar(Object* parentObject):ScrollBar(parentObject)
{
}
VScrollBar::~VScrollBar() { }
void VScrollBar::ScrollTo(Control* ctl)
{
if (ctl && ctl->Parent && ctl->Parent == this->Parent) {
if (ctl->Parent->IsPendLayout()) {
ctl->Parent->RefreshLayout();
}
//控件的矩形位置
const Rect& ctlRect = ctl->GetRect();
if (ctlRect.Y >= 0 && ctlRect.GetBottom() <= Height()) {
return;
}
//出现在顶部
int offset = this->m_offset - ctlRect.Y;
if (ctlRect.Y > 0) {
//出现在底部
offset += this->m_viewLength - ctlRect.Height;
}
__super::ScrollTo(offset, Event::None);
}
}
Rect VScrollBar::GetSliderRect() {
Rect sliderRect;
sliderRect.X = 0;
sliderRect.Y = m_sliderPos;
sliderRect.Width = Width();
sliderRect.Height = m_sliderLength;
if (this->Scrollable() && sliderRect.Height <= 0) {
sliderRect.Height = 1;
}
return sliderRect;
}
void VScrollBar::GetInfo(int* viewLength, int* contentLength, int* scrollBarLength)
{
*viewLength = this->Parent->Height();
*contentLength = this->Parent->GetContentSize().Height;
*scrollBarLength = Height();
}
void VScrollBar::ParentSize(const Size& size) {
this->SetRect({ size.Width - this->Width() ,0,this->Width(),size.Height });
}
void VScrollBar::OnMouseDown(const MouseEventArgs& arg) {
__super::OnMouseDown(arg);
Rect sliderRect = GetSliderRect();
if (sliderRect.IsEmptyArea()) { return; }
auto point = arg.Location;
if (arg.Button == MouseButton::Left && sliderRect.Contains({ point.X,point.Y })) {
m_mouseDown = true;
this->m_lastPoint = point.Y;
}
}
void VScrollBar::OnMouseMove(const MouseEventArgs& arg)
{
__super::OnMouseMove(arg);
if (m_mouseDown) {
auto point = arg.Location;
int offsetY = point.Y - this->m_lastPoint;
m_sliderPos += offsetY;
m_lastPoint = point.Y;
int offset = m_sliderPos * this->m_rollRate + 0.5;
__super::ScrollTo(-offset, Event::OnMouseDrag);
}
}
};

1106
sources/Window.cpp Normal file

File diff suppressed because it is too large Load Diff

111
sources/tinystr.cpp Normal file
View File

@@ -0,0 +1,111 @@
/*
www.sourceforge.net/projects/tinyxml
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef TIXML_USE_STL
#include "tinystr.h"
namespace ezui {
// Error value for find primitive
const TiXmlString::size_type TiXmlString::npos = static_cast<TiXmlString::size_type>(-1);
// Null rep.
TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
void TiXmlString::reserve(size_type cap)
{
if (cap > capacity())
{
TiXmlString tmp;
tmp.init(length(), cap);
memcpy(tmp.start(), data(), length());
swap(tmp);
}
}
TiXmlString& TiXmlString::assign(const char* str, size_type len)
{
size_type cap = capacity();
if (len > cap || cap > 3 * (len + 8))
{
TiXmlString tmp;
tmp.init(len);
memcpy(tmp.start(), str, len);
swap(tmp);
}
else
{
memmove(start(), str, len);
set_size(len);
}
return *this;
}
TiXmlString& TiXmlString::append(const char* str, size_type len)
{
size_type newsize = length() + len;
if (newsize > capacity())
{
reserve(newsize + capacity());
}
memmove(finish(), str, len);
set_size(newsize);
return *this;
}
TiXmlString operator + (const TiXmlString& a, const TiXmlString& b)
{
TiXmlString tmp;
tmp.reserve(a.length() + b.length());
tmp += a;
tmp += b;
return tmp;
}
TiXmlString operator + (const TiXmlString& a, const char* b)
{
TiXmlString tmp;
TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>(strlen(b));
tmp.reserve(a.length() + b_len);
tmp += a;
tmp.append(b, b_len);
return tmp;
}
TiXmlString operator + (const char* a, const TiXmlString& b)
{
TiXmlString tmp;
TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>(strlen(a));
tmp.reserve(a_len + b.length());
tmp.append(a, a_len);
tmp += b;
return tmp;
}
};
#endif // TIXML_USE_STL

1889
sources/tinyxml.cpp Normal file

File diff suppressed because it is too large Load Diff

54
sources/tinyxmlerror.cpp Normal file
View File

@@ -0,0 +1,54 @@
/*
www.sourceforge.net/projects/tinyxml
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "tinyxml.h"
// The goal of the seperate error file is to make the first
// step towards localization. tinyxml (currently) only supports
// english error messages, but the could now be translated.
//
// It also cleans up the code a bit.
//
namespace ezui {
const char* TiXmlBase::errorString[TiXmlBase::TIXML_ERROR_STRING_COUNT] =
{
"No error",
"Error",
"Failed to open file",
"Error parsing Element.",
"Failed to read Element name",
"Error reading Element value.",
"Error reading Attributes.",
"Error: empty tag.",
"Error reading end tag.",
"Error parsing Unknown.",
"Error parsing Comment.",
"Error parsing Declaration.",
"Error document empty.",
"Error null (0) or unexpected EOF found in input stream.",
"Error parsing CDATA.",
"Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
};
}

1645
sources/tinyxmlparser.cpp Normal file

File diff suppressed because it is too large Load Diff