[imgui] 10. 통합 Desktop 구현
2023년 7월 11일
목표 #
- Desktop에 지금까지 구현했던 프로그램들을 전부 로드
- 사실 추가로 구현한거 없이 그냥 연결만 했다.

파일트리 #
지금까지 작업했던 파일들을 복사해서 옮겼다. Own이 붙은것만 직접 작업한 파일이다.
1output
2│ Calendar_Own.cpp
3│ Calendar_Own.hpp
4│ CsvTool_Own.cpp
5│ CsvTool_Own.hpp
6│ Desktop_Own.cpp
7│ Desktop_Own.hpp
8│ DiffViewer_Own.cpp
9│ DiffViewer_Own.hpp
10│ FileExplorer_Own.cpp
11│ FileExplorer_Own.hpp
12│ main.cpp
13│ OtherTopics.cpp
14│ OtherTopics.hpp
15│ Paint_Own.cpp
16│ Paint_Own.hpp
17│ Plotter_Own.cpp
18│ Plotter_Own.hpp
19│ TextEditor_Own.cpp
20│ TextEditor_Own.hpp
21│ UiHelpers.cpp
22│ UiHelpers.hpp
23│ WallClock_Own.cpp
24│ WallClock_Own.hpp
25│ WindowBase.cpp
26└─ WindowBase.hpp
추가작업 #
- CMakeLists.txt에 소스파일 추가하기 (external 폴더의 파일도)
- CMakeLists.txt에 이미지 로드 기능을 정의
- CMakeLists.txt에 헤더경로 지정
- 아이콘으로 띄울 프로그램들을 WindowBase를 상속받도록 구현
- 각 프로그램 연결
구현코드 #
WindowBase #
모든 어플리케이션의 부모클래스이며, 윈도우의 설정을 담당한다.
1class WindowBase
2{
3public:
4 constexpr static auto mainWindowFlags =
5 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
6 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
7 constexpr static auto mainWindowSize = ImVec2(1280.0F, 720.0F);
8 constexpr static auto mainWindowPos = ImVec2(0.0F, 0.0F);
9
10public:
11 WindowBase() : ini(CSimpleIniA()){};
12 virtual ~WindowBase(){};
13
14 // 테마를 세팅, 저장, 로드할 수 있는 함수들
15 void SettingsMenuBar();
16 void LoadTheme();
17 void SaveTheme();
18
19 // 순수가상함수로, 상속받은 객체가 자신의 Draw함수를 구현해야함
20 virtual void Draw(std::string_view label, bool *open = nullptr) = 0;
21
22protected:
23 void DrawColorsSettings(bool *open = nullptr);
24 static ImGuiStyle DefaultColorStyle();
25
26 CSimpleIniA ini;
27};
Desktop::DrawDesktop #
이전과 마찬가지로 그냥 가지고있는 모든 icon을 돌면서 Draw해준다.
1void Desktop::DrawDesktop()
2{
3 for (auto &icon : icons)
4 icon.Draw();
5}
Desktop::Icon::Draw #
아이콘의 Draw는 그냥 버튼을 클릭했을때 base의 Draw를 호출하면서 popupOpen 값을 true로 변경하고 Draw의 open 인자로 전달한다.
1void Desktop::Icon::Draw()
2{
3 constexpr static auto flags =
4 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse;
5
6 ImGui::SetNextWindowPos(position, ImGuiCond_FirstUseEver);
7 ImGui::Begin(fmt::format("###{}", label).data(), nullptr, flags);
8 if (ImGui::Button(label.data(), ImVec2(100.0F, 50.0F)) || popupOpen)
9 {
10 popupOpen = true;
11 base->Draw(label, &popupOpen);
12 }
13 ImGui::End();
14}
TextEditor::Draw #
윈도우의 사이즈, 위치는 WindowBase에 있는대로 초기화하고, Flags는 | 연산자로 추가가 가능하다.
기존 구현한 컨텐츠를 그대로 Draw해주면 된다.
1void TextEditor::Draw(std::string_view label, bool *open)
2{
3 ImGui::SetNextWindowSize(mainWindowSize);
4 ImGui::SetNextWindowPos(mainWindowPos);
5
6 ImGui::Begin(label.data(), open, mainWindowFlags | ImGuiWindowFlags_MenuBar);
7
8 SettingsMenuBar();
9
10 DrawMenu();
11 ImGui::Separator();
12 DrawContent();
13 ImGui::Separator();
14 DrawInfo();
15
16 ImGui::End();
17}
ini 파일 Save/Load #
설정을 좀더 쉽게 저장할 수 있게 ini 파일을 사용했다.
vpkg로 simpleini 패키지를 추가하고, CMakeLists.txt에서 include_directories에 ${SIMPLEINI_INCLUDE_DIRS} 를 추가해주면 SimpleIni.h를 사용할 수 있다.
실제 테마값이 저장된 test.ini 파일 #
1[theme]
2GlobalAlpha = 1.000000
3WindowPaddingX = 8.000000
4WindowPaddingY = 14.000000
5WindowRounding = 0.000000
6FramePaddingX = 4.000000
7FramePaddingY = 3.000000
8...
Save #
test.ini 파일의 theme 섹션에 값을 저장하는 방법
1 const char ini_filepath[] = "test.ini";
2 const char section[] = "theme";
3
4 if (!ImGui::GetCurrentContext())
5 return;
6
7 SI_Error rc = ini.LoadFile(ini_filepath);
8 if (rc < 0)
9 return;
10
11 auto &style = ImGui::GetStyle();
12
13 ini.SetDoubleValue(section, "GlobalAlpha", style.Alpha);
14 ini.SetDoubleValue(section, "WindowPaddingX", style.WindowPadding.x);
15 ini.SetDoubleValue(section, "WindowPaddingY", style.WindowPadding.y);
Load #
1 const char ini_filepath[] = "test.ini";
2 const char section[] = "theme";
3
4 // 파일이 없는경우 기본값 지정 후 파일 만들고 나가기
5 if (!std::filesystem::exists(ini_filepath))
6 {
7 ImGui::GetStyle() = DefaultColorStyle();
8
9 auto out = std::ofstream{ini_filepath};
10 out.close();
11
12 return;
13 }
14
15 ini.LoadFile(ini_filepath);
16 auto &style = ImGui::GetStyle();
17 style.WindowPadding.x = (float)ini.GetDoubleValue(section,
18 "WindowPaddingX",
19 style.WindowPadding.x);
이미지 로드 #
Desktop::DrawBackground #
Desktop에 있는 백그라운드 그리기 함수이다. 커서의 포지션을 0, 0 으로 이동 후 이미지 경로를 전달해서 그린다.
1void Desktop::DrawBackground()
2{
3 ImGui::SetCursorPos(ImVec2(0.0F, 0.0F));
4 const auto image_filepath =
5 fmt::format("{}{}", PROJECT_PATH, "/images/bg.png");
6 LoadAndDisplayImage(image_filepath);
7 ImGui::SetCursorPos(ImVec2(0.0F, 0.0F));
8}
UiHelpers.cpp #
백엔드에 의존하는 코드이기 때문에 구글링으로 내 백엔드에 맞는 코드를 찾아야한다.
loadTexture #
백엔드 코드를 이용해서 텍스쳐를 로드한다. 아래는 OpenGL에서 사용하는 방법이다.
1std::tuple<GLuint, std::uint32_t, std::uint32_t> loadTexture(
2 const char *filename)
3{
4 std::vector<unsigned char> data;
5 std::uint32_t width = 0U;
6 std::uint32_t height = 0U;
7
8 const auto error = lodepng::decode(data, width, height, filename);
9
10 if (error)
11 throw "Error loading image";
12
13 GLuint texture = 0U;
14 glGenTextures(1, &texture);
15 glBindTexture(GL_TEXTURE_2D, texture);
16 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
17 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
18 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
19 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
20
21 glTexImage2D(GL_TEXTURE_2D,
22 0,
23 GL_RGBA,
24 width,
25 height,
26 0,
27 GL_RGBA,
28 GL_UNSIGNED_BYTE,
29 &data[0]);
30
31 return std::make_tuple(texture, width, height);
32}
LoadAndDisplayImage #
이미지를 로드한 후 ImGui에 전달해서 실제로 이미지를 그려주는 작업을 한다.
1void LoadAndDisplayImage(std::string_view image_filepath)
2{
3 try
4 {
5 const auto &[myImageTexture, imageWidth, imageHeight] =
6 loadTexture(image_filepath.data());
7
8 const auto imageSize = ImVec2(static_cast<float>(imageWidth),
9 static_cast<float>(imageHeight));
10 ImGui::Image(reinterpret_cast<ImTextureID>(myImageTexture), imageSize);
11 }
12 catch (const std::exception &e)
13 {
14 std::cerr << e.what() << '\n';
15 exit(1);
16 }
17}
ETC #
TreeNode #
collapse가 가능하고, 활성화되면 내부의 요소들을 Tab으로 포커싱 전환을 할 수 있다.

CollapsingHeader #
collapse가 가능하고, 헤더가 강조된 형식 (영상의 Help 콜랩스)
BeginTabBar #
탭바의 시작함수 BeginTabItem 과 EndTabItem 사이에서 요소를 추가한다.
SeparatorText #
구분선 중간에 텍스트가 있는 형태
BeginMenuBar #
드롭다운 메뉴바 형태
my_imgui_config.h #
CMakeLists.txt에 추가된 코드가 있다.
IMGUI_USER_CONFIG 값을 my_imgui_config.h 파일로 절대경로를 지정했는데,
런타임 옵션을 변경할 수 있는 파일이다.
99% 상황에서는 필요없다고 한다.
1add_compile_definitions(
2 IMGUI_USER_CONFIG="${CMAKE_SOURCE_DIR}/external/my_imgui_config.h"
3)