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()