本文主要介绍ImGui应用中的一些界面优化方法,如果是第一次使用ImGui推荐从上一篇文章开始:使用C++界面框架ImGUI开发一个简单程序,最终的界面效果如下:

使用图标字体

下载IconFontCppHeaders里的IconsFontAwesome6.h文件引入到项目,然后下载fa-solid-900.ttf放到项目根目录,把系统字体目录下的msyh.ttc也移到根目录。项目文件如下:

参考IconFontCppHeaders的示例,字体加载的代码如下:

float baseFontSize = 30.0f;
ImFont* font = io.Fonts->AddFontFromFileTTF
(
"msyh.ttc",
baseFontSize,
nullptr,
io.Fonts->GetGlyphRangesChineseFull()
);
IM_ASSERT(font != nullptr); // FontAwesome字体需要缩小2.0f/3.0f才能正确对齐
float iconFontSize = baseFontSize * 2.0f / 3.0f;
static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_16_FA,0 };
ImFontConfig icons_config;
icons_config.MergeMode = true;
icons_config.PixelSnapH = true;
icons_config.GlyphMinAdvanceX = iconFontSize;
io.Fonts->AddFontFromFileTTF(FONT_ICON_FILE_NAME_FAS, iconFontSize, icons_config, icons_ranges);

使用方法:

ImGui::Text("%s ", ICON_FA_ALIGN_CENTER);
ImGui::Button(ICON_FA_ALIGN_CENTER " Search");

示例中所有图标都已经列出来了方便使用时查看(示例源码见文章末尾的项目链接),如下图所示:

扩展:内存加载字体

先新建一个控制台项目,把ImGui项目misc\fonts路径下的binary_to_compressed_c.cpp文件添加到控制台项目,编译得到binary_to_compressed_c.exe

把需要加载的字体文件(如msyh.ttc)和上面的exe放到同一目录:

在命令行窗口通过cd 命令导航到上面exe的目录,然后输入以下命令(任选一个):

//不压缩,使用C组,源代码大概50M
binary_to_compressed_c.exe -nocompress msyh.ttc msyh >font_msyh.c
//压缩,使用base85,源代码大概25M
binary_to_compressed_c.exe -base85 msyh.ttc msyh >font_msyh.c

因为压缩的源代码编译时报堆栈空间不足,并且实际的数组会大一些,我选择的是不压缩的源代码。

把生成的font_msyh.c添加到自己的项目,在main文件引入字体的命名空间,然后加载字体:

#include "font_msyh.c"

ImFont* font = io.Fonts->AddFontFromMemoryTTF
(
(void*)msyh_data,
msyh_size,
30,
nullptr,
io.Fonts->GetGlyphRangesChineseFull()
);
IM_ASSERT(font != nullptr);

注意msyh_data里面的msyh来自命令行的窗口,注意根据字体自己取一个名字。

结论:使用内存加载字体软件退出时会报错,暂时还解决不了,继续使用本地加载字体的方式 。

隐藏主窗口标题栏

主窗口的标题栏是后端自带的样式且不支持修改,不能自定义样式那就只能隐藏掉,眼不见为净。

本文使用的后端是 glfw+opengl3,ImGui 是 docking 分支,其它项目的实现方法可能会有所不同。

使用 glfw 创建一个 offscreen context ,在创建窗口前加入以下代码即可:

// 设置 offscreen context 的标志位
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Create window with graphics context
GLFWwindow* window = glfwCreateWindow(1280, 720, "演示程序", nullptr, nullptr);

还需要 io.ConfigViewportsNoAutoMerge = true; 开启,同时关闭ConfigViewportsNoTaskBarIcon(默认关闭)。代码如下:

ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
io.ConfigViewportsNoAutoMerge = true;
//io.ConfigViewportsNoTaskBarIcon = true;

注:如果不开启io.ConfigViewportsNoAutoMerge可能引起拖动窗口不小心拖动到隐藏的主窗口, 导致子窗口也隐藏的情况。

做完上面的步骤界面还是隐藏的,那是因为我们的停靠空间开启了填充标志,需要关闭:

void RenderUI()
{
//...
static bool opt_fullscreen = false;
//...
}

参考链接:IMGUI如何去掉外面窗口?

隐藏窗口后会导致一系列问题,比如关闭窗口、最大化等,下面会逐一说明。

增加程序退出

主窗口隐藏后会遇到一个问题,程序该怎么退出?解决思路是显示停靠空间的关闭按钮,检测到关闭按钮点击后关闭主窗口。

Application.h增加GetMainShouldClose函数:

#pragma once
#include "imgui.h"
namespace App
{
static bool MainOpen = true;
//程序是否退出
bool GetMainShouldClose();
//主UI函数,放停靠空间的代码
void RenderUI(); //...其它代码
}

Application.cpp还需要改动RenderUI函数:

bool GetMainShouldClose()
{
return !MainOpen;
}
void RenderUI()
{
bool* p_open = &MainOpen;
static bool opt_fullscreen = false;
//...其它代码
ImGui::Begin("演示程序", p_open, window_flags);
//...其它代码
}

然后再main函数的 App::RenderUI() 后面判断是否退出程序:

//...
App::RenderUI();
if (App::GetMainShouldClose())
{
//关闭窗口,也可以做一些退出确认操作
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
//...

改进HideTabBar

原本的HideTabBar有问题,如果有新窗口停靠到中心位置会导致新窗口被覆盖隐藏,只能通过重置布局恢复。所以标题栏最好不隐藏,只隐藏前面的三角符号,这样也能允许用户自己拖动窗口。

优化代码如下:

void HideTabBar()
{
ImGuiWindowClass window_class;
window_class.DockNodeFlagsOverrideSet = ImGuiDockNodeFlags_NoWindowMenuButton;
//window_class.DockNodeFlagsOverrideSet = ImGuiDockNodeFlags_NoTabBar;
ImGui::SetNextWindowClass(&window_class);
}

窗口最大化

窗口最大化需要知道屏幕的尺寸,这一步涉及到具体的后端代码,需要先在main文件中获取屏幕尺寸。

在main函数前实现GetScreenSize函数,代码如下:

static void GetScreenSize(int &width, int &height)
{
//屏幕数量
int monitorCount;
GLFWmonitor** pMonitor = glfwGetMonitors(&monitorCount);
int screen_x, screen_y;
for (int i = 0; i < monitorCount; i++)
{
const GLFWvidmode* mode = glfwGetVideoMode(pMonitor[i]);
//屏幕大小
screen_x = mode->width;
screen_y = mode->height;
}
width = screen_x;
height = screen_y;
}

然后在main函数中将主窗口的大小设置为屏幕大小一致,代码如下:

 //获取屏幕尺寸
int width, height;
GetScreenSize(width, height);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
// Create window with graphics context
GLFWwindow* window = glfwCreateWindow(width, height, "演示程序", nullptr, nullptr);

在App::RenderUI函数中的停靠空间渲染前设置空间大小,并在主题切换代码后加上大小切换的代码,代码如下:

//...其它代码

//设置最大化、最小化
static bool IsMaximized = true;
static bool LastMaximized = true;
ImVec2 screenSize = ImGui::GetIO().DisplaySize;
if (IsMaximized)
{
if (LastMaximized != IsMaximized)
{
LastMaximized = IsMaximized;
}
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(screenSize);
//隐藏标题栏
//window_flags |= ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
}
else
{
if (LastMaximized != IsMaximized)
{
LastMaximized = IsMaximized;
ImGui::SetNextWindowPos(ImVec2(screenSize[0]*0.1, screenSize[1] * 0.1));
ImGui::SetNextWindowSize(ImVec2(screenSize[0] * 0.8, screenSize[1] * 0.8));
}
} ImGui::Begin(ICON_FA_ANCHOR "演示程序", p_open, window_flags); //...其它代码 //增加主题切换
if (ImGui::BeginMenu(ICON_FA_THERMOMETER "主题(Other)"))
{
if (ImGui::MenuItem("暗黑(Dark)")) { ImGui::StyleColorsDark(); }
if (ImGui::MenuItem("明亮(Light)")) { ImGui::StyleColorsLight(); }
if (ImGui::MenuItem("经典(Classic)")) { ImGui::StyleColorsClassic(); } ImGui::EndMenu();
}
//增加窗口切换
if (ImGui::BeginMenu(ICON_FA_TV "窗口(Windows)"))
{
if (ImGui::MenuItem(ICON_FA_WINDOW_MAXIMIZE "最大(Max)")) { IsMaximized = true; }
if (ImGui::MenuItem(ICON_FA_WINDOW_RESTORE "默认(Default)")) { IsMaximized = false; } ImGui::EndMenu();
}

总结

文中的主要的优化措施就是引入字体图标隐藏主窗口,结合前一篇的文章,ImGui基本的功能都已经涉及到了。

文中还有一些遗留问题,比如:

  • 全屏无法覆盖系统的任务栏
  • 标题栏没有自定义
  • 内存加载字体报错
  • 任务栏窗口图标自定义

目前的内容对于简单的项目来说基本够用,后面应该不会再继续更新ImGui相关的内容了,等项目中遇到问题再说。

项目源码链接:提取码: tv7n

ImGui界面优化:使用图标字体、隐藏主窗口标题栏的更多相关文章

  1. Electron实用技巧-开机启动时隐藏主窗口,只显示系统托盘

    # 1 在桌面软件中,开机自启动是很常见的功能,在electron中也提供了很好的支持,以下是主要代码: //应用是否打包if (app.isPackaged) {  //设置开机启动  app.se ...

  2. compiz隐藏最大化窗口标题栏

    xfwm换了compiz试试,还行,挺方便.就是这个隐藏最大化窗口的标题栏没有现成的ui设置项,google到如下解决方案: 修改后立即生效. https://planetkris.com/2009/ ...

  3. Qt Widgets——主窗口及其主要组成部分

    Main Window and Related Classes QAction 动作类,用于当做一个菜单项或工具项插入菜单或工具栏 QActionGroup 动作组,用于管理多个动作,设置它们之间的互 ...

  4. 【MFC】如何在MFC创建的程序中更改主窗口的属性 与 父窗口 WS_CLIPCHILDREN 样式 对子窗口刷新的影响 与 窗体区域绘制问题WS_CLIPCHILDREN与WS_CLIPSIBLINGS

    如何在MFC创建的程序中更改主窗口的属性 摘自:http://blog.sina.com.cn/s/blog_4bebc4830100aq1m.html 在MFC创建的单文档界面中: (基于对话框的, ...

  5. Qt5 主窗口组成

    1. 菜单栏 菜单是一系列命令的列表.为了实现菜单.工具栏按钮.键盘快捷键等命令的一致性,Qt使用动作(Action)来表示这些命令.Qt的菜单就是由一系列的QAction动作对象构成的列表,而菜单栏 ...

  6. SecureCRT字体、界面优化

    SecureCRT字体.界面优化 本文是secureCRT的第三篇博文,也是目前secureCRT优化的最终篇.首次使用该软件时候.应该会设置字体和编码,接下来,将演示如何设置. 1. 字体.编码设置 ...

  7. 基于Metronic的Bootstrap开发框架经验总结(10)--优化Bootstrap图标管理

    在基于Bootstrap开发的项目中,鲜艳颜色的按钮,以及丰富的图表是很吸引人的特点,为了将这个特点发挥到极致,可以利用Bootstrap图标抽取到数据库里面,并在界面中进行管理和使用,这样我们可以把 ...

  8. 01_MUI之Boilerplate中:HTML5演示样例,动态组件,自己定义字体演示样例,自己定义字体演示样例,图标字体演示样例

     1安装HBuilder5.0.0,安装后的界面截图例如以下: 2 依照https://www.muicss.com/docs/v1/css-js/boilerplate-html中的说明,创建上 ...

  9. webfont应用系列(二)如何制作图标字体?

    工具: Adobe Illustrator CS5 Fontographer 5.1,下载地址 1.打开Fontographer,菜单"File"->"New&qu ...

  10. (原创)IconFont(矢量图标字体)在Winform中的应用

    一.前言 很多时候,使用矢量图形可以带来非常美观的界面效果,比如SVG的使用.但是Winform原生是不支持显示SVG图像的,所以退而求其次,可以使用IconFont来实现相似的矢量效果. 先来个图解 ...

随机推荐

  1. 基于DevExpress的GridControl实现的一些界面处理功能

    DevExpress的GridControl控件能够提供很多强大的操作,其视图GridView能够通过各种设置,呈现出多种复杂的界面效果,本篇随笔探讨一些常见的GridControl控件及其GridV ...

  2. Django4全栈进阶之路22 项目实战(三种方式开发部门管理):方式三:FBV+ModelForm+get_object_or_404

    1.视图 @login_required def department_list_view(request): departments = Department.objects.all() retur ...

  3. Django-Virtualenv虚拟环境安装、新建,激活和手动指定Python解释器、虚拟环境安装Django、创建Django项目、运行Django项目

    一.安装虚拟环境: 命令:pip3 install virtualenv 二.安装管理工具: 命令:pip3 install virtualenvwrapper 三.新建: 命令:python -m ...

  4. ComboBox1 绑定手动创建的DataTable

    '************************************************** '*过程名称:DT_PAFORMAL '*功能说明:员工类别 '**************** ...

  5. props传值遇Cannot read property getAttribute of undefined异常

    今有一个echarts 图标的子组件使用watch 监听接受父组件传入的data,而在父组件页面再次根据日期筛选数据,重新传入子组件进行图表重绘时老实会提示报错 vue.runtime.esm.js? ...

  6. Java的标识符、关键字、 常量、变量、数据类型、 数据类型转换、字符ASCII码表(基础语法的学习)

    一.Java的关键字和标识符 关键字 1.定义: 在Java程序中,已经定义好的被预先使用的一些特殊的单词称为关键字,一共有50个关键字(48+2个保留字),关键字都是小写的英文单词 2.关键字的分类 ...

  7. VSCode 中利用 Remote SSH 连接远程服务器

    北京时间 2019 年 5 月 3 日,在 PyCon 2019 大会上,微软发布了 VS Code Remote.这是一个用来实现远程开发的功能插件,对于许多使用 Windows 进行开发,但是需要 ...

  8. 六大云端 Jupyter Notebook 平台测评

    有许多方法可以与其他人共享静态 Jupyter 笔记本,例如把它发布在 GitHub 上或通过 nbviewer 链接进行分享. 但是,如果接收人已经安装了 Jupyter Notebook 环境,那 ...

  9. 在DevExpress的GridView的列中,使用RepositoryItemSearchLookUpEdit控件实现产品列表信息的展示和选择

    有时候,我们为了方便,我们往往使用扩展函数的代码方式创建很多GridView的操作功能,如在随笔<在DevExpress中使用BandedGridView表格实现多行表头的处理>中介绍过多 ...

  10. 揭秘 Task.Wait

    目录 简介 背后的实现 Task.Wait 的两个阶段 SpinWait 阶段 BlockingWait 阶段 Task.Wait 可能会导致的问题 可能会导致线程池饥饿 可能会导致死锁 .NET 6 ...