[imgui] 9. Desktop 구현

목표

  • Windows 처럼 여러 창을 띄워둘 수 있는 Desktop 구현
  • 윈도우 버튼에서 모든 애플리케이션을 실행할 수 있다.
  • 시계 기능

Desktop
Desktop

구현코드

github 링크

메인함수

class Desktop
{
public:
    // 아이콘 숫자 기본값 10개
    constexpr static auto numIcons = std::uint32_t{10};

    // 아이콘 구조체. Draw메서드로 아이콘윈도우를 그린다.
    struct Icon
    {
        Icon(std::string_view label_)
            : label(label_), position(ImVec2{}), popupOpen(false),
              clickCount(0){};

        void Draw();

        std::string label;
        ImVec2 position;
        bool popupOpen;
        std::uint32_t clickCount;
    };

public:
    // 10개의 아이콘을 초기화. 아이콘 label은 Icon{num} 형식
    Desktop() : icons({}), clock({})
    {
        icons.reserve(numIcons);
        for (std::uint32_t i = 0; i < numIcons; i++)
            icons.push_back(Icon{fmt::format("Icon{}", i)});
    }
    void Draw(std::string_view label);

private:
    void DrawDesktop();     // 아이콘 10개를 그려주는 함수
    void DrawTaskbar();     // 작업표시줄을 그려주는 함수

    void ShowIconList(bool *open = nullptr);    // 작업표시줄 윈도우키 눌렀을때 메뉴

private:
    std::vector<Icon> icons;
    Clock clock;            // Clock 객체
};

별거 없다.

void Desktop::Draw(std::string_view label)
{
    constexpr static auto window_flags =
        ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
        ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar |
        ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs;
    constexpr static auto window_size = ImVec2(1280.0F, 720.0F);
    constexpr static auto window_pos = ImVec2(0.0F, 0.0F);

    ImGui::SetNextWindowSize(window_size);
    ImGui::SetNextWindowPos(window_pos);

    ImGui::Begin(label.data(), nullptr, window_flags);

    DrawDesktop();
    DrawTaskbar();

    ImGui::End();

}

DrawDesktop

그냥 전체 아이콘 돌면서 Draw함수를 호출해주기만 한다.

void Desktop::DrawDesktop()
{
    for (auto &icon : icons)
        icon.Draw();
}

Icon::Draw

아이콘에 각각의 윈도우를 배정해주고 그려준다.

void Desktop::Icon::Draw()
{
    constexpr static auto icon_window_flags = ImGuiWindowFlags_NoCollapse |
                                          ImGuiWindowFlags_NoScrollbar;

    constexpr static auto button_size = ImVec2(100.0f, 50.0f);

    const auto label_icon_window = fmt::format("IconWindow##{}", label);
    const auto label_icon_popup = fmt::format("IconPopup##{}", label);
    
    // 윈도우나 위젯이 최초로 생성될때만 함수를 실행시키는 플래그 
    // 최초라는건 컴파일 이후 처음 실행됐을때를 의미한다. 
    // 이후엔 상태값이 imgui.ini 파일에 저장되어 다시켰을때 같은값으로 출력된다. 
    ImGui::SetNextWindowPos(position, ImGuiCond_FirstUseEver);

    // 항상 함수를 실행시키는 플래그. 기본값 None과 동일한 동작을한다
    ImGui::SetNextWindowSize(
        ImVec2(button_size.x + 35.0f, button_size.y + 35.0f),
        ImGuiCond_Always);

    ImGui::Begin(label_icon_window.data(), nullptr, icon_window_flags);
    // 버튼을 클릭하면 팝업 띄우기
    if (ImGui::Button(label.data(), button_size))
        ++clickCount;
    if (clickCount >= 1 || popupOpen)
    {
        ImGui::OpenPopup(label_icon_popup.data());
        clickCount = 0;
        popupOpen = true;
    }

    if (ImGui::BeginPopupModal(label_icon_popup.data(), &popupOpen))
    {
        ImGui::Text("Hi");
        if (ImGui::Button("Close"))
        {
            popupOpen = false;
            ImGui::CloseCurrentPopup();
        }
        ImGui::EndPopup();
    }
    ImGui::End();
}

DrawTaskbar

void WindowClass::DrawTaskbar()
{
    constexpr static auto taskbar_flags =
        ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
        ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar |
        ImGuiWindowFlags_NoTitleBar;
    constexpr static auto taskbar_size = ImVec2(1280.0F, 40.0F);
    constexpr static auto taskbar_pos = ImVec2(0.0F, 680.0F);

    static auto open_taskbar = false;

    ImGui::SetNextWindowSize(taskbar_size);
    ImGui::SetNextWindowPos(taskbar_pos);

    ImGui::Begin("Taskbar", nullptr, taskbar_flags);

    // 작업표시줄의 윈도우버튼 같은것
    if (ImGui::Button("All Icons", ImVec2(100, 30)))
    {
        ImGui::OpenPopup("My Programs");
        open_taskbar = true;
    }

    if (open_taskbar)
        ShowIconList(&open_taskbar);

    ImGui::SameLine();

    // 오른쪽 끝에 시계 표시
    ImGui::SetCursorPosX(ImGui::GetContentRegionAvail().x);

    static auto clock_open = false;
    clock.GetTime();
    const auto time = fmt::format("{}:{}", clock.hrs, clock.mins);

    // 버튼에 시간을 그리고, 시계 클릭하면 아날로그 시계 그려줌
    if (ImGui::Button(time.data(), ImVec2(100.0F, 30.0F)) || clock_open)
    {
        clock.Draw("clockWindow");
        clock_open = true;
    }

    // 아무데나 클릭하면 시계 종료
    if (clock_open && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
        clock_open = false;

    ImGui::End();
}

ShowIconList

void WindowClass::ShowIconList(bool *open)
{
    // 한 요소의 높이를 미리 구한다.
    // 함수역할: 현재 폰트 스타일로 계산된 줄간격이 포함된 한 라인의 높이
    const auto selectable_height = ImGui::GetTextLineHeightWithSpacing();
    // 한요소의 높이 * 전체요소수 + Taskbar의 높이
    const auto popup_height = selectable_height * numIcons + 40.0F;

    ImGui::SetNextWindowSize(ImVec2(100.0F, popup_height), ImGuiCond_Always);
    ImGui::SetNextWindowPos(ImVec2(0.0F, 680.0F - popup_height),
                            ImGuiCond_Always);

    if (ImGui::BeginPopupModal("My Programs", open, ImGuiWindowFlags_NoResize))
    {
        for (auto &icon : icons)
        {
            if (ImGui::Selectable(icon.label.data()))
            {
                icon.popupOpen = true;
                ImGui::CloseCurrentPopup();
            }
        }

        ImGui::EndPopup();
    }
}

Comments

ESC
Type to search...