Step 184: first-run wizard
This commit is contained in:
@@ -1,11 +1,5 @@
|
||||
#pragma once
|
||||
// Included inside CodeEditorWidget (selection + editing).
|
||||
struct MultiCursor {
|
||||
int cursor = 0;
|
||||
int selStart = -1;
|
||||
int selEnd = -1;
|
||||
};
|
||||
|
||||
void syncPrimaryToMulti() {
|
||||
if (cursors_.empty()) {
|
||||
cursors_.push_back({cursor_, selStart_, selEnd_});
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
struct MultiCursor {
|
||||
int cursor = 0;
|
||||
int selStart = -1;
|
||||
int selEnd = -1;
|
||||
};
|
||||
|
||||
struct CodeEditorOptions {
|
||||
bool showWhitespace = false;
|
||||
bool readOnly = false;
|
||||
|
||||
@@ -1943,7 +1943,7 @@ struct EditorState {
|
||||
}
|
||||
}
|
||||
|
||||
bool selectionRange(int& outStart, int& outEnd) const {
|
||||
bool selectionRange(int& outStart, int& outEnd) {
|
||||
if (!active()) return false;
|
||||
if (!active()->widget.hasSelectionRange()) return false;
|
||||
active()->widget.getSelectionRange(outStart, outEnd);
|
||||
|
||||
116
editor/src/FirstRunWizard.h
Normal file
116
editor/src/FirstRunWizard.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
#include "ThemeEngine.h"
|
||||
#include "EditorState.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct FirstRunWizardState {
|
||||
bool open = false;
|
||||
int step = 0;
|
||||
int layoutIndex = 0;
|
||||
int themeIndex = 0;
|
||||
int keyIndex = 0;
|
||||
};
|
||||
|
||||
static void renderFirstRunWizard(EditorState& state, FirstRunWizardState& wizard) {
|
||||
if (!wizard.open) return;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(520, 360), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("Setup Wizard", &wizard.open, ImGuiWindowFlags_NoResize)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
const char* layoutLabels[] = {"VSCode", "Emacs", "JetBrains"};
|
||||
const LayoutPreset layoutValues[] = {LayoutPreset::VSCode, LayoutPreset::Emacs, LayoutPreset::JetBrains};
|
||||
const char* keyLabels[] = {"VSCode", "JetBrains", "Emacs"};
|
||||
const KeybindingProfile keyValues[] = {KeybindingProfile::VSCode, KeybindingProfile::JetBrains, KeybindingProfile::Emacs};
|
||||
std::vector<std::string> themeNames = ThemeEngine::instance().listThemes();
|
||||
if (themeNames.empty()) {
|
||||
themeNames = {"Whetstone Dark", "Whetstone Light"};
|
||||
}
|
||||
int maxThemes = std::min(4, (int)themeNames.size());
|
||||
|
||||
if (wizard.step == 0) {
|
||||
ImGui::TextUnformatted("Welcome to Whetstone");
|
||||
ImGui::Separator();
|
||||
ImGui::TextWrapped("This short wizard helps you pick a layout, theme, and keybindings.");
|
||||
} else if (wizard.step == 1) {
|
||||
ImGui::TextUnformatted("Choose a layout preset");
|
||||
ImGui::Separator();
|
||||
for (int i = 0; i < IM_ARRAYSIZE(layoutLabels); ++i) {
|
||||
if (ImGui::RadioButton(layoutLabels[i], wizard.layoutIndex == i)) {
|
||||
wizard.layoutIndex = i;
|
||||
}
|
||||
}
|
||||
} else if (wizard.step == 2) {
|
||||
ImGui::TextUnformatted("Choose a theme");
|
||||
ImGui::Separator();
|
||||
for (int i = 0; i < maxThemes; ++i) {
|
||||
if (ImGui::RadioButton(themeNames[i].c_str(), wizard.themeIndex == i)) {
|
||||
wizard.themeIndex = i;
|
||||
}
|
||||
}
|
||||
} else if (wizard.step == 3) {
|
||||
ImGui::TextUnformatted("Choose keybindings");
|
||||
ImGui::Separator();
|
||||
for (int i = 0; i < IM_ARRAYSIZE(keyLabels); ++i) {
|
||||
if (ImGui::RadioButton(keyLabels[i], wizard.keyIndex == i)) {
|
||||
wizard.keyIndex = i;
|
||||
}
|
||||
}
|
||||
} else if (wizard.step == 4) {
|
||||
ImGui::TextUnformatted("Get started");
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("Open Example Project")) {
|
||||
state.notify(NotificationLevel::Info, "Example project not bundled yet.");
|
||||
}
|
||||
if (ImGui::Button("Open Folder")) {
|
||||
auto path = FileDialog::openFolder({"Open Folder", state.workspaceRoot});
|
||||
if (!path.empty()) {
|
||||
state.workspaceRoot = path;
|
||||
state.fileTreeDirty = true;
|
||||
state.search.projectSearch.setRoot(state.workspaceRoot);
|
||||
state.refreshBuildSystem();
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("New File")) {
|
||||
std::string lang = state.active() ? state.active()->language : "python";
|
||||
state.createBuffer(state.makeUntitledName(), "", lang);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (wizard.step > 0) {
|
||||
if (ImGui::Button("Back")) {
|
||||
wizard.step = std::max(0, wizard.step - 1);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (wizard.step < 4) {
|
||||
if (ImGui::Button("Next")) {
|
||||
wizard.step = std::min(4, wizard.step + 1);
|
||||
}
|
||||
} else {
|
||||
if (ImGui::Button("Finish")) {
|
||||
if (wizard.layoutIndex >= 0 && wizard.layoutIndex < IM_ARRAYSIZE(layoutValues)) {
|
||||
state.ui.layoutPreset = layoutValues[wizard.layoutIndex];
|
||||
}
|
||||
if (wizard.keyIndex >= 0 && wizard.keyIndex < IM_ARRAYSIZE(keyValues)) {
|
||||
state.keys.setProfile(keyValues[wizard.keyIndex]);
|
||||
state.registerCommands();
|
||||
}
|
||||
if (wizard.themeIndex >= 0 && wizard.themeIndex < maxThemes) {
|
||||
state.settings.setTheme(themeNames[wizard.themeIndex]);
|
||||
ThemeEngine::instance().applyTheme(themeNames[wizard.themeIndex]);
|
||||
}
|
||||
state.saveSettingsToDisk();
|
||||
wizard.open = false;
|
||||
state.ui.showFirstRunWizard = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -20,7 +20,7 @@ inline std::unordered_map<std::string, RichTooltipState>& richTooltipStates() {
|
||||
return states;
|
||||
}
|
||||
|
||||
inline void renderMarkdownLine(const std::string& line, ImFont* monoFont) {
|
||||
inline void richRenderMarkdownLine(const std::string& line, ImFont* monoFont) {
|
||||
std::string trimmed = line;
|
||||
while (!trimmed.empty() && (trimmed.back() == '\r' || trimmed.back() == '\n')) {
|
||||
trimmed.pop_back();
|
||||
@@ -42,11 +42,11 @@ inline void renderMarkdownLine(const std::string& line, ImFont* monoFont) {
|
||||
}
|
||||
}
|
||||
|
||||
inline void renderMarkdown(const std::string& text, ImFont* monoFont) {
|
||||
inline void richRenderMarkdown(const std::string& text, ImFont* monoFont) {
|
||||
std::istringstream ss(text);
|
||||
std::string line;
|
||||
while (std::getline(ss, line)) {
|
||||
renderMarkdownLine(line, monoFont);
|
||||
richRenderMarkdownLine(line, monoFont);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ inline void renderRichTooltip(const std::string& id,
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * alpha);
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + maxWidth);
|
||||
renderMarkdown(content, monoFont);
|
||||
richRenderMarkdown(content, monoFont);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::Separator();
|
||||
if (ImGui::SmallButton("Pin")) {
|
||||
@@ -86,7 +86,7 @@ inline void renderRichTooltip(const std::string& id,
|
||||
if (ImGui::Begin(("Tooltip##" + id).c_str(), &open, flags)) {
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + maxWidth);
|
||||
ImGui::BeginChild(("##tip_scroll_" + id).c_str(), ImVec2(maxWidth, maxHeight), false);
|
||||
renderMarkdown(state.pinnedContent, monoFont);
|
||||
richRenderMarkdown(state.pinnedContent, monoFont);
|
||||
ImGui::EndChild();
|
||||
ImGui::PopTextWrapPos();
|
||||
if (ImGui::SmallButton("Close")) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "EditorUtils.h"
|
||||
#include "CompletionUtils.h"
|
||||
#include "ThemeEngine.h"
|
||||
#include "FirstRunWizard.h"
|
||||
|
||||
// --- Panel headers ---
|
||||
#include "panels/MenuBarPanel.h"
|
||||
@@ -188,12 +189,15 @@ int main(int, char**) {
|
||||
}
|
||||
io.FontGlobalScale = state.settings.getFontSize() / baseFontSize;
|
||||
SessionData session;
|
||||
if (state.loadSession(session)) {
|
||||
bool hasSession = state.loadSession(session);
|
||||
if (hasSession) {
|
||||
if (!session.imguiIni.empty()) {
|
||||
ImGui::LoadIniSettingsFromMemory(session.imguiIni.c_str(),
|
||||
session.imguiIni.size());
|
||||
}
|
||||
state.applySession(session);
|
||||
} else {
|
||||
state.ui.showFirstRunWizard = true;
|
||||
}
|
||||
state.lspTransport = std::make_shared<NullLSPTransport>();
|
||||
state.lsp = std::make_shared<LSPClient>(state.lspTransport);
|
||||
@@ -370,6 +374,10 @@ int main(int, char**) {
|
||||
renderBottomPanel(state);
|
||||
renderMemoryStrategiesPanel(state);
|
||||
renderStatusBar(state);
|
||||
static FirstRunWizardState wizard;
|
||||
wizard.open = state.ui.showFirstRunWizard;
|
||||
renderFirstRunWizard(state, wizard);
|
||||
state.ui.showFirstRunWizard = wizard.open;
|
||||
state.notifications.renderHistoryWindow([&](const Notification& note) {
|
||||
if (note.hasTarget) state.navigateToTarget(note.target);
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ struct UIFlags {
|
||||
bool showLineNumbers = true;
|
||||
bool showLspSettings = false;
|
||||
bool showSettingsPanel = false;
|
||||
bool showFirstRunWizard = false;
|
||||
int bottomTab = 0; // 0=Output,1=AST,2=Highlighted
|
||||
LayoutPreset layoutPreset = LayoutPreset::VSCode;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user