本文主要介绍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. Netty自定义协议要素

    魔数:用来判断是否是无效数据包 协议版本号:可以支持协议的升级 序列化算法:消息正文使用哪种序列化方式,可以扩展.例如:protobuf,json,hessian等 指令类型:跟业务相关,例如:登录, ...

  2. Can't uninstall 'Pillow'. No files were found to uninstall.

    Can't uninstall 'Pillow'. No files were found to uninstall. Pillow卸载不掉的解决办法 1.进入python所在路径,进入scripts ...

  3. 限速神器RateLimiter源码解析

    作者:京东科技 李玉亮 目录指引 限流场景 软件系统中一般有两种场景会用到限流: •场景一.高并发的用户端场景. 尤其是C端系统,经常面对海量用户请求,如不做限流,遇到瞬间高并发的场景,则可能压垮系统 ...

  4. ICLR 2018-A Simple Neural Attentive Meta-Learner

    Key 时序卷积+注意力机制(前者从过去的经验中收集信息,而后者则精确定位具体的信息.) 解决的主要问题 手工设计的限制:最近的许多元学习方法都是大量手工设计的,要么使用专门用于特定应用程序的架构,要 ...

  5. 【Java】Java代码拷贝文件的速度

    Java代码拷贝文件的速度究竟有多快? 前言 最近学习Java到了流处理,其中有种流叫FileInputStream和FileOutputStream,简单来说,就是操作文件的,老师给我们示范了一个非 ...

  6. How to use the shell command to get the version of Linux Distributions All In One

    How to use the shell command to get the version of Linux Distributions All In One 如何使用 shell 命令获取 Li ...

  7. uniapp 全局背景音乐播放+暂停(跳转页面不暂停)

    最近需要一个功能 是在h5中播放小游戏的背景音乐,但是跳转界面之后音乐不暂停,就是跳转多个页面之后,音乐依然在播放,在游戏界面会有设置的静音的按钮,可以开启音乐和关闭音乐. 单独建了一个music.j ...

  8. Python进行大文件的备份

    Python进行大文件的备份的思路:每次仅从原文件中读取指定字符的内容后写入新文件,然后循环操作. def copy_big_file(): # 接收用户输入的文件名 old_file = input ...

  9. OpenAI发布ChatGPT函数调用和API更新

    2023年6月13日,OpenAI针对开发者调用的API做了重大更新,包括更易操控的 API模型.函数调用功能.更长的上下文和更低的价格. 在今年早些时候发布gpt-3.5-turbo,gpt-4在短 ...

  10. 让AI支持游戏更自然、更直观:基于情感分析的AI游戏体验和交互设计

    目录 引言 随着游戏市场的不断发展,人工智能技术的应用也越来越广泛.其中,情感分析技术在游戏中的应用,可以让游戏更加自然.直观,同时提高游戏的用户体验.本文将介绍如何让 AI 支持游戏更自然.更直观: ...