[imgui] 3. TextEditor 구현

[imgui] 3. TextEditor 구현

2023년 7월 6일
imgui

목표 #

  • 텍스트 편집이 가능한 텍스트 에디터
  • 파일의 줄 수를 표시
  • 텍스트파일 저장과 로드 기능 (키보드 단축키까지)
  • 현재 열린 파일명, 확장자 확인가능 TextEditor

구현 코드 #

메인함수 #

 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}
comments powered by Disqus