Line data Source code
1 : #include "gui/views/gui_snapshots.h"
2 : #include "gui/theme/theme.h"
3 : #include <imgui.h>
4 : #include <cstdio>
5 : #include <algorithm>
6 :
7 : namespace Amplitron {
8 :
9 12 : void GuiSnapshots::render() {
10 12 : const SnapshotsProps& p = props_;
11 :
12 16 : float bar_height = ImGui::GetFrameHeight()
13 12 : + ImGui::GetStyle().WindowPadding.y * 2.0f
14 12 : + ImGui::GetStyle().WindowBorderSize * 2.0f;
15 :
16 12 : ImGui::BeginChild("SnapshotBar", ImVec2(0, bar_height), true,
17 : ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
18 :
19 : // Vertically center the button row
20 4 : {
21 12 : float avail_y = ImGui::GetContentRegionAvail().y;
22 12 : float row_h = ImGui::GetFrameHeight();
23 12 : float pad_y = std::max(0.0f, (avail_y - row_h) * 0.5f);
24 12 : ImGui::SetCursorPosY(ImGui::GetCursorPosY() + pad_y);
25 : }
26 :
27 12 : ImGui::TextColored(Theme::TextSecondary(), "SNAPSHOTS");
28 :
29 60 : for (int i = 0; i < SnapshotManager::NUM_SLOTS; ++i) {
30 48 : ImGui::SameLine();
31 :
32 48 : const auto& slot = p.slots[i];
33 48 : const bool is_active = slot.is_active;
34 48 : const bool is_filled = slot.is_filled;
35 48 : const char* lbl = slot.label;
36 :
37 48 : if (is_active) {
38 12 : ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.50f, 0.42f, 0.20f, 1.0f));
39 12 : ImGui::PushStyleColor(ImGuiCol_ButtonHovered, Theme::GoldHot());
40 12 : ImGui::PushStyleColor(ImGuiCol_ButtonActive, Theme::Gold());
41 40 : } else if (is_filled) {
42 12 : ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.22f, 0.20f, 0.16f, 1.0f));
43 12 : ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.35f, 0.30f, 0.18f, 1.0f));
44 12 : ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.45f, 0.38f, 0.18f, 1.0f));
45 4 : } else {
46 24 : ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.14f, 0.13f, 0.12f, 1.0f));
47 24 : ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.20f, 0.19f, 0.16f, 1.0f));
48 24 : ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.25f, 0.23f, 0.19f, 1.0f));
49 : }
50 :
51 16 : char btn_id[16];
52 48 : std::snprintf(btn_id, sizeof(btn_id), "%s##snap_%d", lbl, i);
53 :
54 48 : if (ImGui::Button(btn_id)) {
55 0 : if (is_filled) {
56 0 : if (p.on_recall_slot) p.on_recall_slot(i);
57 0 : std::snprintf(status_msg_, sizeof(status_msg_), "Recalled %s", lbl);
58 0 : status_timer_ = STATUS_DISPLAY_SECONDS;
59 0 : }
60 0 : }
61 :
62 48 : ImGui::PopStyleColor(3);
63 :
64 : // Tooltip
65 48 : if (ImGui::IsItemHovered()) {
66 0 : if (is_filled)
67 0 : ImGui::SetTooltip("Left-click to recall snapshot %s (or Ctrl+%d)\nRight-click to overwrite or clear", lbl, i + 1);
68 : else
69 0 : ImGui::SetTooltip("Slot %s is empty\nRight-click to save current board here", lbl);
70 0 : }
71 :
72 : // Right-click context menu
73 16 : char popup_id[24];
74 48 : std::snprintf(popup_id, sizeof(popup_id), "SnapCtx_%d", i);
75 48 : if (ImGui::BeginPopupContextItem(popup_id)) {
76 0 : char save_label[40];
77 0 : std::snprintf(save_label, sizeof(save_label), "Save current board to %s", lbl);
78 0 : if (ImGui::MenuItem(save_label)) {
79 0 : if (p.on_save_slot) p.on_save_slot(i);
80 0 : std::snprintf(status_msg_, sizeof(status_msg_), "Saved to %s", lbl);
81 0 : status_timer_ = STATUS_DISPLAY_SECONDS;
82 0 : }
83 0 : if (is_filled) {
84 0 : char clear_label[24];
85 0 : std::snprintf(clear_label, sizeof(clear_label), "Clear %s", lbl);
86 0 : ImGui::Separator();
87 0 : if (ImGui::MenuItem(clear_label)) {
88 0 : if (p.on_clear_slot) p.on_clear_slot(i);
89 0 : }
90 0 : }
91 0 : ImGui::EndPopup();
92 0 : }
93 16 : }
94 :
95 12 : ImGui::SameLine();
96 :
97 12 : if (status_timer_ > 0.0f) {
98 9 : float alpha = std::min(status_timer_, 1.0f);
99 9 : ImGui::TextColored(ImVec4(0.90f, 0.78f, 0.39f, alpha), " %s", status_msg_);
100 9 : status_timer_ -= ImGui::GetIO().DeltaTime;
101 3 : } else {
102 3 : ImGui::TextColored(Theme::TextDim(),
103 : " Left-click to recall | Right-click to save / clear | Ctrl+1-4 to recall");
104 : }
105 :
106 12 : ImGui::EndChild();
107 12 : }
108 :
109 : } // namespace Amplitron
|