[imgui] 3. TextEditor 구현
2023년 7월 6일
목표 #
- 텍스트 편집이 가능한 텍스트 에디터
- 파일의 줄 수를 표시
- 텍스트파일 저장과 로드 기능 (키보드 단축키까지)
- 현재 열린 파일명, 확장자 확인가능

구현 코드 #
메인함수 #
1#include <cstring>
2#include <string>
3#include <cstdint>
4#include <string_view>
5#include <filesystem>
6
7namespace fs = std::filesystem;
8
9class WindowClass
10{
11public:
12 // 코드에서 사용할 상수값들 초기화
13 static constexpr auto bufferSize = std::size_t{1024};
14
15 static constexpr auto popUpFlags =
16 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
17 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
18 static constexpr auto popUpSize = ImVec2(300.0f, 100.0f);
19 static constexpr auto popUpButtonSize = ImVec2(120.0f, 0.0f);
20 static constexpr auto popUpPos = ImVec2(1280.0f / 2.0f - popUpSize.x / 2.0f,
21 720.0f / 2.0f - popUpSize.y / 2.0f);
22
23public:
24 WindowClass() : currentFilename({})
25 {
26 std::memset(textBuffer, 0, bufferSize);
27 }
28
29 void Draw(std::string_view label);
30
31private:
32 void DrawMenu();
33 void DrawContent();
34 void DrawInfo();
35
36 void DrawSavePopup();
37 void DrawLoadPopup();
38
39 // 실제 저장, 로드 기능
40 void SaveToFile(std::string_view filename);
41 void LoadFromFile(std::string_view filename);
42
43 std::string GetFileExtension(std::string_view filename);
44
45private:
46 // 텍스트에디터의 텍스트버퍼
47 char textBuffer[bufferSize];
48 std::string currentFilename;
49};
50
51void render(WindowClass &window_obj);
1void WindowClass::Draw(std::string_view label)
2{
3 constexpr static auto window_flags =
4 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
5 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
6 constexpr static auto window_size = ImVec2(1280.0F, 720.0F);
7 constexpr static auto window_pos = ImVec2(0.0F, 0.0F);
8
9 ImGui::SetNextWindowSize(window_size);
10 ImGui::SetNextWindowPos(window_pos);
11
12 ImGui::Begin(label.data(), nullptr, window_flags);
13
14 DrawMenu();
15 ImGui::Separator();
16
17 DrawContent();
18 ImGui::Separator();
19
20 DrawInfo();
21
22 ImGui::End();
23}
DrawMenu #
세이브, 로드, 클리어 버튼이 있는 메뉴바
1void WindowClass::DrawMenu()
2{
3 // 현재 입력된 키를 확인
4 const auto ctrl_pressed = ImGui::GetIO().KeyCtrl;
5 const auto esc_pressed =
6 ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape));
7 const auto s_pressed =
8 ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_S));
9 const auto l_pressed =
10 ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_L));
11
12 // Save 버튼을 누르거나 Ctrl + S 가 눌렸을때
13 if (ImGui::Button("Save") || (ctrl_pressed && s_pressed))
14 {
15 // 현재 파일이 없는 새파일인 경우에만 Save 팝업 띄우기
16 if (currentFilename.empty())
17 ImGui::OpenPopup("Save File");
18 else
19 SaveToFile(currentFilename);
20 }
21
22 ImGui::SameLine();
23
24 if (ImGui::Button("Load") || (ctrl_pressed && l_pressed))
25 ImGui::OpenPopup("Load File");
26
27 ImGui::SameLine();
28
29 // Clear 버튼이 눌린경우 textBuffer 초기화
30 if (ImGui::Button("Clear"))
31 std::memset(textBuffer, 0, bufferSize);
32
33 DrawSavePopup();
34 DrawLoadPopup();
35}
DrawPopup #
세이브 로직에서 띄워지는 팝업창
1void WindowClass::DrawSavePopup()
2{
3 static char saveFilenameBuffer[256];
4 const auto esc_pressed =
5 ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape));
6
7 // Popup의 Window 사이즈와 위치를 중앙에 표시되도록 설정
8 ImGui::SetNextWindowSize(popUpSize);
9 ImGui::SetNextWindowPos(popUpPos);
10 if (ImGui::BeginPopupModal("Save File", nullptr, popUpFlags))
11 {
12 ImGui::InputText("Filename",
13 saveFilenameBuffer,
14 sizeof(saveFilenameBuffer));
15
16 if (ImGui::Button("Save", popUpButtonSize))
17 {
18 SaveToFile(saveFilenameBuffer);
19 currentFilename = saveFilenameBuffer;
20 ImGui::CloseCurrentPopup();
21 }
22
23 ImGui::SameLine();
24
25 // 버튼이 눌리거나 ESC 키가 눌린경우 팝업닫기
26 if (ImGui::Button("Cancel", popUpButtonSize) || esc_pressed)
27 {
28 memset(saveFilenameBuffer, 0, 256);
29 ImGui::CloseCurrentPopup();
30 }
31 ImGui::EndPopup();
32 }
33}
파일을 로드할때 띄워지는 팝업. 세이브 팝업과 크게 다르지 않다.
1void WindowClass::DrawLoadPopup()
2{
3 static char loadFilenameBuffer[256];
4 const auto esc_pressed =
5 ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape));
6
7 ImGui::SetNextWindowSize(popUpSize);
8 ImGui::SetNextWindowPos(popUpPos);
9 if (ImGui::BeginPopupModal("Load File", nullptr, popUpFlags))
10 {
11 ImGui::InputText("Filename",
12 loadFilenameBuffer,
13 sizeof(loadFilenameBuffer));
14
15 if (ImGui::Button("Load", popUpButtonSize))
16 {
17 LoadFromFile(loadFilenameBuffer);
18 currentFilename = loadFilenameBuffer;
19 ImGui::CloseCurrentPopup();
20 }
21
22 ImGui::SameLine();
23
24 if (ImGui::Button("Cancel", popUpButtonSize) || esc_pressed)
25 {
26 ImGui::CloseCurrentPopup();
27 }
28 ImGui::EndPopup();
29 }
30}
SaveToFile / LoadFromFile #
실제 데이터를 파일에 저장하거나 로드해오는 로직
1void WindowClass::SaveToFile(std::string_view filename)
2{
3 // 파일스트림을 열어서 textBuffer를 전부 저장하고 닫는다.
4 auto out = std::ofstream{filename.data()};
5 if (out.is_open())
6 {
7 out << textBuffer;
8 out.close();
9 }
10}
11
12void WindowClass::LoadFromFile(std::string_view filename)
13{
14 auto in = std::ifstream{filename.data()};
15 if (in.is_open())
16 {
17 auto buffer = std::stringstream{};
18 buffer << in.rdbuf();
19 std::memcpy(textBuffer, buffer.str().data(), bufferSize);
20 in.close();
21 }
22}
DrawContent #
텍스트를 작성할 수 있는 입력필드와 줄 수를 표시해주는 라인카운터
1void WindowClass::DrawContent()
2{
3 static constexpr auto inputTextSize = ImVec2(1200.0f, 625.0f);
4 static constexpr auto lineNumberSize = ImVec2(30.0f, inputTextSize.y);
5 static constexpr auto inputTextFlags =
6 ImGuiInputTextFlags_AllowTabInput |
7 ImGuiInputTextFlags_NoHorizontalScroll;
8
9 // 라인카운터는 여러개의 Text 요소로 만들어져 있어서 Child로 하나로 묶음
10 ImGui::BeginChild("LineNumbers", lineNumberSize);
11
12 const auto line_count = std::count(textBuffer, textBuffer + bufferSize, '\n') + 1;
13 for (auto i = 1; i <= line_count; ++i)
14 ImGui::Text("%d", i);
15
16 ImGui::EndChild();
17
18 // 이전 요소는 Text의 마지막 요소가 아닌,
19 // 여러 Text가 묶인 Child 요소 하나를 이전요소로 판단
20 ImGui::SameLine();
21 ImGui::InputTextMultiline("###inputField",
22 textBuffer,
23 bufferSize,
24 inputTextSize,
25 inputTextFlags);
26}
DrawInfo #
그냥 하단에 파일명, 파일 확장자를 표시해준다.
1void WindowClass::DrawInfo()
2{
3 if (currentFilename.size() == 0)
4 {
5 ImGui::Text("No File Opened");
6 return;
7 }
8 const auto file_extionsion = GetFileExtension(currentFilename);
9 ImGui::Text("Opened file %s | File extionsion %s",
10 currentFilename.data(),
11 file_extionsion.data());
12}