In dieser Lektion lernen wir, Menüs darzustellen. Wir erstellen ein Programm ähnlich dem Windows-Notepad mit einer oberen Menüleiste und einem Untermenü „Speichern“. Klickt man auf Speichern, wird der gesamte Text aus dem unteren Eingabefeld in eine Datei geschrieben. Zusätzlich beheben wir das Problem mit verstümmelten Sonderzeichen und zeigen, wie man Schriftarten konfiguriert: ohne diese Einstellung werden nicht-englische Zeichen nur als Fragezeichen angezeigt.
Beim Speichern öffnet sich ein systemeigener Datei-Speicherdialog. Der Nutzer wählt ein Speicherverzeichnis, gibt einen Dateinamen ein und kann anschließend die Textdatei abspeichern.
Hier sehen Sie einen Screenshot des laufenden Programms:

Das Programm bietet grundlegende Bearbeitungs- und Speicherfunktionen wie das Windows-eigene Notepad, die kompilierte Ausführungsdatei ist extrem klein.
Bei Kompilierung im Release-Modus beträgt die Dateigröße nur 645 KB.

Sehen wir uns nun den vollständigen Quellcode an:
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
#pragma execution_character_set("utf-8")
#include <GLFW/glfw3.h>
#include "imgui.h"
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_opengl3.h"
#include <cstdio>
#include <string>
#include <fstream>
#include <windows.h>
#include <io.h>
#include <commdlg.h> // Benötigter Header für Dateidialoge
// Maximale Größe des Textpuffers
const int TEXT_BUFFER_MAX = 4096;
std::string editor_text;
// Text in Datei schreiben mit UTF-8 BOM
// Schreibt den Textinhalt in die angegebene Datei
bool SaveTextToFile(const char* filepath, const std::string& text)
{
std::ofstream out_file(filepath, std::ios::out | std::ios::binary);
if (!out_file.is_open())
return false;
const unsigned char utf8_bom[] = { 0xEF, 0xBB, 0xBF };
out_file.write((char*)utf8_bom, sizeof(utf8_bom));
out_file << text.c_str(); // Nur gültigen Text bis zum Nullterminator ausgeben
out_file.close();
return true;
}
// System-Speicherdialog anzeigen, gibt den gewählten Dateipfad zurück
bool ShowSaveFileDialog(char* out_path, DWORD out_size)
{
OPENFILENAMEA ofn; // Nutze die ANSI-Version A
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = nullptr;
ofn.lpstrFilter = "Textdateien\0*.txt\0Alle Dateien\0*.*\0"; // Filter für TXT oder alle Formate
ofn.lpstrFile = out_path;
ofn.nMaxFile = out_size;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT;
ofn.lpstrDefExt = "txt";
return GetSaveFileNameA(&ofn); // ANSI-Dialogfunktion aufrufen
}
// Schrift einrichten, um verstümmelte Fragezeichen zu beheben
// Passenden Schriftpfad für Ihre Umgebung eintragen oder Systemstandardschrift verwenden
void SetupFont(ImGuiIO& io)
{
// Gängige chinesische Schrift unter Windows
const char* font_path = "C:/Windows/Fonts/msyh.ttc"; // Microsoft YaHei
float font_size = 18.0f;
// Bereich für häufig genutzte vereinfachte chinesische Zeichen laden
ImVector<ImWchar> ranges;
ImFontGlyphRangesBuilder builder;
builder.AddRanges(io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
builder.BuildRanges(&ranges);
// Benutzerdefinierte Schrift laden
io.Fonts->AddFontFromFileTTF(font_path, font_size, nullptr, ranges.Data);
// Bei mehrsprachiger Unterstützung weitere Bereiche hinzufügen:
// io.Fonts->AddFontFromFileTTF(font_path, font_size, nullptr, io.Fonts->GetGlyphRangesJapanese());
// io.Fonts->AddFontFromFileTTF(font_path, font_size, nullptr, io.Fonts->GetGlyphRangesKorean());
}
int main()
{
SetConsoleOutputCP(65001);
// Wichtig: Puffer vergrößern und komplett mit Null füllen, um Speichermüll zu entfernen und ???-Zeichen zu reparieren
editor_text.resize(TEXT_BUFFER_MAX, '\0');
if (!glfwInit())
{
printf("GLFW Initialisierung fehlgeschlagen\n");
return -1;
}
const char* glsl_version = "#version 330";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "Text Notiz Editor", nullptr, nullptr);
if (!window)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
ImGui::StyleColorsLight();
SetupFont(io); // Schrift laden, um Zeichenverstümmelung zu beheben
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
bool show_save_success = false;
bool show_save_fail = false;
char path[MAX_PATH] = "";// Puffer für den gespeicherten Dateipfad
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
// Strg+S Speicher-Tastenkürzel
if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_S))
{
if (ShowSaveFileDialog(path, MAX_PATH))
{
if (SaveTextToFile(path, editor_text))
show_save_success = true;
else
show_save_fail = true;
}
}
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(io.DisplaySize);
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_MenuBar; // Pflichtflag, um die Menüleiste anzuzeigen
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::Begin("Text Notiz Editor", nullptr, window_flags);
// Oberste Menüleiste rendern
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Datei"))
{
if (ImGui::MenuItem("Speichern", "Strg+S")) // Untermenü Speichern: Text + Tastenkürzel-Hinweis
{
// Gleiche Logik wie bei Buttons aus vorherigen Lektionen, Code läuft bei Menüklick
if (ShowSaveFileDialog(path, MAX_PATH)) // Speicherdialog öffnen
{
if (SaveTextToFile(path, editor_text)) // Datei speichern
show_save_success = true; // Erfolgsflag setzen für spätere Benachrichtigung
else
show_save_fail = true;
}
}
ImGui::Separator();
if (ImGui::MenuItem("Beenden", "Esc")) // Menüpunkt zum Schließen des Programms
{
glfwSetWindowShouldClose(window, true); // Schließflag setzen, Programm beenden
}
ImGui::EndMenu();