cmdr2's notes

A simple browser-like shell using ImGui and GLFW. It was supposed to show a webview, but I couldn't figure out how to embed a webview in the window (instead of it popping up in its own window). Maybe I'll revisit this in the future if I can figure it out.

Create a folder named thirdparty (alongside main.cpp and CMakeLists.txt) and clone the git repositories for imgui and glfw into the thirdparty folder.

Then compile using:

cmake -B build
cmake --build build --config Release

And run the compiled executable.

main.cpp:

#include <vector>
#include <string>
#include <memory>
#include <stdexcept>
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <GLFW/glfw3.h>

#include <iostream>

struct Tab {
    std::string url;

    Tab(const std::string& initial_url)
        : url(initial_url) {
    }
};

class Browser {
public:
    Browser() {
        tabs.emplace_back("https://example.com"); // Default tab
        current_tab = 0;
    }

    void run() {
        if (!glfwInit()) {
            throw std::runtime_error("Failed to initialize GLFW");
        }

        GLFWwindow* window = glfwCreateWindow(1280, 720, "Tabbed Browser", nullptr, nullptr);
        if (!window) {
            glfwTerminate();
            throw std::runtime_error("Failed to create GLFW window");
        }

        glfwMakeContextCurrent(window);
        glfwSwapInterval(1);

        ImGui::CreateContext();
        ImGui_ImplGlfw_InitForOpenGL(window, true);
        ImGui_ImplOpenGL3_Init("#version 130");

        while (!glfwWindowShouldClose(window)) {
            glfwPollEvents();
            render(window);
        }

        ImGui_ImplOpenGL3_Shutdown();
        ImGui_ImplGlfw_Shutdown();
        ImGui::DestroyContext();
        glfwDestroyWindow(window);
        glfwTerminate();
    }

private:
    std::vector<Tab> tabs;
    int current_tab;

    void render(GLFWwindow* window) {
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        ImGui::SetNextWindowPos(ImVec2(0, 0));
        ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
        ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);

        ImGui::Begin("Browser", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove);

        if (ImGui::BeginTabBar("Tabs")) {
            for (size_t i = 0; i < tabs.size(); ++i) {
                bool open = true;
                if (ImGui::BeginTabItem(("Tab " + std::to_string(i + 1)).c_str(), &open)) {
                    current_tab = static_cast<int>(i);
                    ImGui::Text("URL %d: %s", i, tabs[i].url.c_str());
                    ImGui::EndTabItem();
                }
                if (!open) {
                    tabs.erase(tabs.begin() + i);
                    if (current_tab >= static_cast<int>(i)) {
                        current_tab = std::max(0, current_tab - 1);
                    }
                }
            }

            // Add new tab button
            if (ImGui::TabItemButton("+")) {
                tabs.emplace_back("https://example.com");
                current_tab = static_cast<int>(tabs.size()) - 1;
            }

            ImGui::EndTabBar();
        }

        // Add the ≡ button
        ImGui::SameLine();
        if (ImGui::Button("≡")) {
            ImGui::OpenPopup("TabMenu");
        }

        // Menu popup logic
        if (ImGui::BeginPopup("TabMenu")) {
            if (ImGui::MenuItem("New Tab")) {
                tabs.emplace_back("https://example.com");
                current_tab = static_cast<int>(tabs.size()) - 1;
            }
            if (ImGui::MenuItem("New Private Tab")) {
                tabs.emplace_back("https://example.com?private=1");
                current_tab = static_cast<int>(tabs.size()) - 1;
            }
            if (ImGui::MenuItem("Settings")) {
                tabs.emplace_back("https://settings.example.com");
                current_tab = static_cast<int>(tabs.size()) - 1;
            }
            ImGui::EndPopup();
        }

        ImGui::End();
        ImGui::PopStyleVar(3);

        ImGui::Render();
        int display_w, display_h;
        glfwGetFramebufferSize(window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        glfwSwapBuffers(window);
    }
};

int main() {
    try {
        Browser browser;
        browser.run();
    } catch (const std::exception& e) {
        fprintf(stderr, "Error: %s\n", e.what());
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)

# Project name and version
project(TabbedBrowser VERSION 1.0)

# Set C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")

add_subdirectory(thirdparty/glfw)

set(SOURCES main.cpp)

set(IMGUI_SOURCES
    thirdparty/imgui/imgui.cpp
	thirdparty/imgui/imgui_draw.cpp
	thirdparty/imgui/imgui_tables.cpp
	thirdparty/imgui/imgui_widgets.cpp
	thirdparty/imgui/imgui_demo.cpp
	thirdparty/imgui/backends/imgui_impl_glfw.cpp
	thirdparty/imgui/backends/imgui_impl_opengl3.cpp
)

# Platform-specific settings
if(WIN32)
    set(PLATFORM_LIBS glfw opengl32)
elseif(APPLE)
    find_library(COCOA_LIBRARY Cocoa REQUIRED)
    find_library(IOKIT_LIBRARY IOKit REQUIRED)
    find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED)
    find_library(COREGRAPHICS_LIBRARY CoreGraphics REQUIRED)
    set(PLATFORM_LIBS glfw ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREFOUNDATION_LIBRARY} ${COREGRAPHICS_LIBRARY})
elseif(UNIX)
    find_package(X11 REQUIRED)
    set(PLATFORM_LIBS glfw X11 GL)
endif()

# Add executable
add_executable(TabbedBrowser ${SOURCES} ${IMGUI_SOURCES})

# Link libraries
target_include_directories(TabbedBrowser PRIVATE thirdparty/imgui)
target_include_directories(TabbedBrowser PRIVATE thirdparty/imgui/backends)
target_link_libraries(TabbedBrowser PRIVATE ${PLATFORM_LIBS})

# Include directories
if(APPLE)
    include_directories(/usr/local/include)
    link_directories(/usr/local/lib)
endif()