mirror of
https://github.com/obsqrbtz/clrsync.git
synced 2026-04-09 12:37:41 +03:00
chore :refactored remaining views
This commit is contained in:
23
src/gui/widgets/centered_text.cpp
Normal file
23
src/gui/widgets/centered_text.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "centered_text.hpp"
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
void centered_text(const std::string& text, const ImVec4& color)
|
||||
{
|
||||
float window_width = ImGui::GetContentRegionAvail().x;
|
||||
float text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::TextColored(color, "%s", text.c_str());
|
||||
}
|
||||
|
||||
void centered_buttons(float total_width, const std::function<void()>& render_buttons)
|
||||
{
|
||||
float window_width = ImGui::GetContentRegionAvail().x;
|
||||
float start_pos = (window_width - total_width) * 0.5f;
|
||||
if (start_pos > 0)
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + start_pos);
|
||||
render_buttons();
|
||||
}
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
16
src/gui/widgets/centered_text.hpp
Normal file
16
src/gui/widgets/centered_text.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef CLRSYNC_GUI_WIDGETS_CENTERED_TEXT_HPP
|
||||
#define CLRSYNC_GUI_WIDGETS_CENTERED_TEXT_HPP
|
||||
|
||||
#include "imgui.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
void centered_text(const std::string& text, const ImVec4& color = ImVec4(1, 1, 1, 1));
|
||||
void centered_buttons(float total_width, const std::function<void()>& render_buttons);
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
|
||||
#endif // CLRSYNC_GUI_WIDGETS_CENTERED_TEXT_HPP
|
||||
71
src/gui/widgets/error_message.cpp
Normal file
71
src/gui/widgets/error_message.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "error_message.hpp"
|
||||
#include "colors.hpp"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
void error_message::set(const std::string& message)
|
||||
{
|
||||
m_message = message;
|
||||
}
|
||||
|
||||
void error_message::clear()
|
||||
{
|
||||
m_message.clear();
|
||||
}
|
||||
|
||||
bool error_message::has_error() const
|
||||
{
|
||||
return !m_message.empty();
|
||||
}
|
||||
|
||||
const std::string& error_message::get() const
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
|
||||
void error_message::render(const core::palette& palette)
|
||||
{
|
||||
if (m_message.empty())
|
||||
return;
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
auto error_bg_color = palette_color(palette, "error");
|
||||
auto error_text_color = palette_color(palette, "on_error");
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, error_bg_color);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, error_bg_color);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 4.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);
|
||||
|
||||
if (ImGui::BeginChild("##error_box", ImVec2(0, 0),
|
||||
ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders))
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, error_text_color);
|
||||
ImGui::TextWrapped("Error: %s", m_message.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button,
|
||||
ImVec4(error_bg_color.x * 0.8f, error_bg_color.y * 0.8f,
|
||||
error_bg_color.z * 0.8f, error_bg_color.w));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(error_bg_color.x * 0.6f, error_bg_color.y * 0.6f,
|
||||
error_bg_color.z * 0.6f, error_bg_color.w));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, error_text_color);
|
||||
|
||||
if (ImGui::Button("Dismiss##error"))
|
||||
m_message.clear();
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
25
src/gui/widgets/error_message.hpp
Normal file
25
src/gui/widgets/error_message.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef CLRSYNC_GUI_WIDGETS_ERROR_MESSAGE_HPP
|
||||
#define CLRSYNC_GUI_WIDGETS_ERROR_MESSAGE_HPP
|
||||
|
||||
#include "core/palette/palette.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
class error_message
|
||||
{
|
||||
public:
|
||||
void set(const std::string& message);
|
||||
void clear();
|
||||
bool has_error() const;
|
||||
const std::string& get() const;
|
||||
void render(const core::palette& palette);
|
||||
|
||||
private:
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
|
||||
#endif // CLRSYNC_GUI_WIDGETS_ERROR_MESSAGE_HPP
|
||||
@@ -181,4 +181,66 @@ void form_field::set_path_browse_callback(const std::function<std::string(const
|
||||
m_path_browse_callback = callback;
|
||||
}
|
||||
|
||||
bool form_field::render_slider(const form_field_config& config, int& value)
|
||||
{
|
||||
render_label(config);
|
||||
|
||||
if (config.field_width > 0)
|
||||
ImGui::SetNextItemWidth(config.field_width);
|
||||
else if (config.field_width < 0)
|
||||
ImGui::SetNextItemWidth(config.field_width);
|
||||
|
||||
std::string id = "##" + config.label;
|
||||
std::string format = config.format.empty() ? "%d" : config.format.c_str();
|
||||
int old_value = value;
|
||||
bool changed = ImGui::SliderInt(id.c_str(), &value, (int)config.min_value, (int)config.max_value, format.c_str());
|
||||
|
||||
if (config.show_reset)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
std::string reset_id = "Reset##" + config.label;
|
||||
if (ImGui::Button(reset_id.c_str()))
|
||||
{
|
||||
value = config.default_value;
|
||||
changed = (old_value != value);
|
||||
}
|
||||
}
|
||||
|
||||
render_tooltip(config);
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool form_field::render_combo(const form_field_config& config, const std::vector<std::string>& items, int& selected_idx, std::string& value)
|
||||
{
|
||||
render_label(config);
|
||||
|
||||
if (config.field_width > 0)
|
||||
ImGui::SetNextItemWidth(config.field_width);
|
||||
else if (config.field_width < 0)
|
||||
ImGui::SetNextItemWidth(config.field_width);
|
||||
|
||||
std::string id = "##" + config.label;
|
||||
bool changed = false;
|
||||
|
||||
if (ImGui::BeginCombo(id.c_str(), value.c_str()))
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(items.size()); i++)
|
||||
{
|
||||
bool is_selected = (i == selected_idx);
|
||||
if (ImGui::Selectable(items[i].c_str(), is_selected))
|
||||
{
|
||||
selected_idx = i;
|
||||
value = items[i];
|
||||
changed = true;
|
||||
}
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
render_tooltip(config);
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
@@ -12,7 +13,9 @@ enum class field_type
|
||||
text,
|
||||
text_with_hint,
|
||||
number,
|
||||
slider,
|
||||
path,
|
||||
combo,
|
||||
readonly_text
|
||||
};
|
||||
|
||||
@@ -27,6 +30,9 @@ struct form_field_config
|
||||
std::string hint;
|
||||
float min_value = 0.0f;
|
||||
float max_value = 100.0f;
|
||||
std::string format;
|
||||
bool show_reset = false;
|
||||
int default_value = 0;
|
||||
};
|
||||
|
||||
class form_field
|
||||
@@ -41,6 +47,12 @@ class form_field
|
||||
bool render_number(const form_field_config& config, int& value);
|
||||
bool render_number(const form_field_config& config, float& value);
|
||||
|
||||
// Render a slider field
|
||||
bool render_slider(const form_field_config& config, int& value);
|
||||
|
||||
// Render a combo box field
|
||||
bool render_combo(const form_field_config& config, const std::vector<std::string>& items, int& selected_idx, std::string& value);
|
||||
|
||||
// Render a path input field with browse button
|
||||
bool render_path(const form_field_config& config, std::string& value);
|
||||
|
||||
|
||||
28
src/gui/widgets/link_button.cpp
Normal file
28
src/gui/widgets/link_button.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "link_button.hpp"
|
||||
#include "imgui.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
void open_url(const std::string& url)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::string cmd = "start " + url;
|
||||
#elif __APPLE__
|
||||
std::string cmd = "open " + url;
|
||||
#else
|
||||
std::string cmd = "xdg-open " + url;
|
||||
#endif
|
||||
std::system(cmd.c_str());
|
||||
}
|
||||
|
||||
bool link_button(const std::string& label, const std::string& url, float width)
|
||||
{
|
||||
bool clicked = ImGui::Button(label.c_str(), ImVec2(width, 0));
|
||||
if (clicked)
|
||||
open_url(url);
|
||||
return clicked;
|
||||
}
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
14
src/gui/widgets/link_button.hpp
Normal file
14
src/gui/widgets/link_button.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef CLRSYNC_GUI_WIDGETS_LINK_BUTTON_HPP
|
||||
#define CLRSYNC_GUI_WIDGETS_LINK_BUTTON_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
void open_url(const std::string& url);
|
||||
bool link_button(const std::string& label, const std::string& url, float width = 0.0f);
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
|
||||
#endif // CLRSYNC_GUI_WIDGETS_LINK_BUTTON_HPP
|
||||
17
src/gui/widgets/section_header.cpp
Normal file
17
src/gui/widgets/section_header.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "section_header.hpp"
|
||||
#include "colors.hpp"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
void section_header(const std::string& title, const core::palette& palette)
|
||||
{
|
||||
ImGui::Spacing();
|
||||
auto accent_color = palette_color(palette, "accent");
|
||||
ImGui::TextColored(accent_color, "%s", title.c_str());
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
14
src/gui/widgets/section_header.hpp
Normal file
14
src/gui/widgets/section_header.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef CLRSYNC_GUI_WIDGETS_SECTION_HEADER_HPP
|
||||
#define CLRSYNC_GUI_WIDGETS_SECTION_HEADER_HPP
|
||||
|
||||
#include "core/palette/palette.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
void section_header(const std::string& title, const core::palette& palette);
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
|
||||
#endif // CLRSYNC_GUI_WIDGETS_SECTION_HEADER_HPP
|
||||
56
src/gui/widgets/settings_buttons.cpp
Normal file
56
src/gui/widgets/settings_buttons.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "settings_buttons.hpp"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
void settings_buttons::render(const settings_buttons_callbacks& callbacks, bool apply_enabled)
|
||||
{
|
||||
ImGui::Spacing();
|
||||
|
||||
float spacing = ImGui::GetStyle().ItemSpacing.x;
|
||||
float window_width = ImGui::GetContentRegionAvail().x;
|
||||
float total_buttons_width = 4 * m_button_width + 3 * spacing;
|
||||
float start_pos = (window_width - total_buttons_width) * 0.5f;
|
||||
|
||||
if (start_pos > 0)
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + start_pos);
|
||||
|
||||
if (ImGui::Button("OK", ImVec2(m_button_width, 0)))
|
||||
{
|
||||
if (callbacks.on_ok)
|
||||
callbacks.on_ok();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (!apply_enabled)
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f);
|
||||
|
||||
if (ImGui::Button("Apply", ImVec2(m_button_width, 0)) && apply_enabled)
|
||||
{
|
||||
if (callbacks.on_apply)
|
||||
callbacks.on_apply();
|
||||
}
|
||||
|
||||
if (!apply_enabled)
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Reset", ImVec2(m_button_width, 0)))
|
||||
{
|
||||
if (callbacks.on_reset)
|
||||
callbacks.on_reset();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Cancel", ImVec2(m_button_width, 0)))
|
||||
{
|
||||
if (callbacks.on_cancel)
|
||||
callbacks.on_cancel();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
30
src/gui/widgets/settings_buttons.hpp
Normal file
30
src/gui/widgets/settings_buttons.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef CLRSYNC_GUI_WIDGETS_SETTINGS_BUTTONS_HPP
|
||||
#define CLRSYNC_GUI_WIDGETS_SETTINGS_BUTTONS_HPP
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
struct settings_buttons_callbacks
|
||||
{
|
||||
std::function<void()> on_ok;
|
||||
std::function<void()> on_apply;
|
||||
std::function<void()> on_reset;
|
||||
std::function<void()> on_cancel;
|
||||
};
|
||||
|
||||
class settings_buttons
|
||||
{
|
||||
public:
|
||||
void render(const settings_buttons_callbacks& callbacks, bool apply_enabled);
|
||||
|
||||
void set_button_width(float width) { m_button_width = width; }
|
||||
|
||||
private:
|
||||
float m_button_width = 100.0f;
|
||||
};
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
|
||||
#endif // CLRSYNC_GUI_WIDGETS_SETTINGS_BUTTONS_HPP
|
||||
181
src/gui/widgets/template_controls.cpp
Normal file
181
src/gui/widgets/template_controls.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
#include "template_controls.hpp"
|
||||
#include "colors.hpp"
|
||||
#include "styled_checkbox.hpp"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
template_controls::template_controls() = default;
|
||||
|
||||
void template_controls::render(template_control_state& state,
|
||||
const template_control_callbacks& callbacks,
|
||||
const core::palette& palette,
|
||||
validation_message& validation)
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 8));
|
||||
|
||||
render_action_buttons(state, callbacks, palette);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
render_fields(state, callbacks);
|
||||
|
||||
validation.render(palette);
|
||||
}
|
||||
|
||||
void template_controls::render_action_buttons(template_control_state& state,
|
||||
const template_control_callbacks& callbacks,
|
||||
const core::palette& palette)
|
||||
{
|
||||
if (ImGui::Button(" + New "))
|
||||
{
|
||||
if (callbacks.on_new)
|
||||
callbacks.on_new();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Create a new template");
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(" Save "))
|
||||
{
|
||||
if (callbacks.on_save)
|
||||
callbacks.on_save();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Save template (Ctrl+S)");
|
||||
|
||||
if (state.is_editing_existing)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
auto error = palette_color(palette, "error");
|
||||
auto error_hover = ImVec4(error.x * 1.1f, error.y * 1.1f, error.z * 1.1f, error.w);
|
||||
auto error_active = ImVec4(error.x * 0.8f, error.y * 0.8f, error.z * 0.8f, error.w);
|
||||
auto on_error = palette_color(palette, "on_error");
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, error);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, error_hover);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, error_active);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, on_error);
|
||||
if (ImGui::Button(" Delete "))
|
||||
{
|
||||
if (callbacks.on_delete)
|
||||
callbacks.on_delete();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Delete this template");
|
||||
ImGui::PopStyleColor(4);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10);
|
||||
|
||||
bool old_enabled = state.enabled;
|
||||
styled_checkbox checkbox;
|
||||
checkbox.render("Enabled", &state.enabled, palette,
|
||||
state.enabled ? checkbox_style::success : checkbox_style::error);
|
||||
|
||||
if (old_enabled != state.enabled && state.is_editing_existing)
|
||||
{
|
||||
if (callbacks.on_enabled_changed)
|
||||
callbacks.on_enabled_changed(state.enabled);
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Enable/disable this template for theme application");
|
||||
}
|
||||
|
||||
void template_controls::render_fields(template_control_state& state,
|
||||
const template_control_callbacks& callbacks)
|
||||
{
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("Name:");
|
||||
ImGui::SameLine(80);
|
||||
ImGui::SetNextItemWidth(180.0f);
|
||||
char name_buf[256] = {0};
|
||||
snprintf(name_buf, sizeof(name_buf), "%s", state.name.c_str());
|
||||
if (ImGui::InputText("##template_name", name_buf, sizeof(name_buf)))
|
||||
{
|
||||
state.name = name_buf;
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Unique name for this template");
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("Input:");
|
||||
ImGui::SameLine(80);
|
||||
ImGui::SetNextItemWidth(-120.0f);
|
||||
char input_path_buf[512] = {0};
|
||||
snprintf(input_path_buf, sizeof(input_path_buf), "%s", state.input_path.c_str());
|
||||
if (ImGui::InputTextWithHint("##input_path", "Path to template file...", input_path_buf,
|
||||
sizeof(input_path_buf)))
|
||||
{
|
||||
state.input_path = input_path_buf;
|
||||
if (state.is_editing_existing && callbacks.on_input_path_changed)
|
||||
callbacks.on_input_path_changed(state.input_path);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Browse##input"))
|
||||
{
|
||||
if (callbacks.on_browse_input)
|
||||
{
|
||||
std::string selected = callbacks.on_browse_input(state.input_path);
|
||||
if (!selected.empty())
|
||||
{
|
||||
state.input_path = selected;
|
||||
if (state.is_editing_existing && callbacks.on_input_path_changed)
|
||||
callbacks.on_input_path_changed(state.input_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Path where the template source file is stored");
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("Output:");
|
||||
ImGui::SameLine(80);
|
||||
ImGui::SetNextItemWidth(-120.0f);
|
||||
char path_buf[512] = {0};
|
||||
snprintf(path_buf, sizeof(path_buf), "%s", state.output_path.c_str());
|
||||
if (ImGui::InputTextWithHint("##output_path", "Path for generated config...", path_buf,
|
||||
sizeof(path_buf)))
|
||||
{
|
||||
state.output_path = path_buf;
|
||||
if (state.is_editing_existing && callbacks.on_output_path_changed)
|
||||
callbacks.on_output_path_changed(state.output_path);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Browse##output"))
|
||||
{
|
||||
if (callbacks.on_browse_output)
|
||||
{
|
||||
std::string selected = callbacks.on_browse_output(state.output_path);
|
||||
if (!selected.empty())
|
||||
{
|
||||
state.output_path = selected;
|
||||
if (state.is_editing_existing && callbacks.on_output_path_changed)
|
||||
callbacks.on_output_path_changed(state.output_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Path where the processed config will be written");
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("Reload:");
|
||||
ImGui::SameLine(80);
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
char reload_buf[512] = {0};
|
||||
snprintf(reload_buf, sizeof(reload_buf), "%s", state.reload_command.c_str());
|
||||
if (ImGui::InputTextWithHint("##reload_cmd", "Command to reload app (optional)...", reload_buf,
|
||||
sizeof(reload_buf)))
|
||||
{
|
||||
state.reload_command = reload_buf;
|
||||
if (state.is_editing_existing && callbacks.on_reload_command_changed)
|
||||
callbacks.on_reload_command_changed(state.reload_command);
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("Shell command to run after applying theme (e.g., 'pkill -USR1 kitty')");
|
||||
}
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
59
src/gui/widgets/template_controls.hpp
Normal file
59
src/gui/widgets/template_controls.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef CLRSYNC_GUI_WIDGETS_TEMPLATE_CONTROLS_HPP
|
||||
#define CLRSYNC_GUI_WIDGETS_TEMPLATE_CONTROLS_HPP
|
||||
|
||||
#include "core/palette/palette.hpp"
|
||||
#include "gui/widgets/form_field.hpp"
|
||||
#include "gui/widgets/validation_message.hpp"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
struct template_control_state
|
||||
{
|
||||
std::string name;
|
||||
std::string input_path;
|
||||
std::string output_path;
|
||||
std::string reload_command;
|
||||
bool enabled{true};
|
||||
bool is_editing_existing{false};
|
||||
};
|
||||
|
||||
struct template_control_callbacks
|
||||
{
|
||||
std::function<void()> on_new;
|
||||
std::function<void()> on_save;
|
||||
std::function<void()> on_delete;
|
||||
std::function<void(bool)> on_enabled_changed;
|
||||
std::function<std::string(const std::string&)> on_browse_input;
|
||||
std::function<std::string(const std::string&)> on_browse_output;
|
||||
std::function<void(const std::string&)> on_input_path_changed;
|
||||
std::function<void(const std::string&)> on_output_path_changed;
|
||||
std::function<void(const std::string&)> on_reload_command_changed;
|
||||
};
|
||||
|
||||
class template_controls
|
||||
{
|
||||
public:
|
||||
template_controls();
|
||||
|
||||
void render(template_control_state& state,
|
||||
const template_control_callbacks& callbacks,
|
||||
const core::palette& palette,
|
||||
validation_message& validation);
|
||||
|
||||
private:
|
||||
void render_action_buttons(template_control_state& state,
|
||||
const template_control_callbacks& callbacks,
|
||||
const core::palette& palette);
|
||||
|
||||
void render_fields(template_control_state& state,
|
||||
const template_control_callbacks& callbacks);
|
||||
|
||||
form_field m_form;
|
||||
};
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
|
||||
#endif // CLRSYNC_GUI_WIDGETS_TEMPLATE_CONTROLS_HPP
|
||||
40
src/gui/widgets/validation_message.cpp
Normal file
40
src/gui/widgets/validation_message.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "validation_message.hpp"
|
||||
#include "colors.hpp"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
void validation_message::set(const std::string& message)
|
||||
{
|
||||
m_message = message;
|
||||
}
|
||||
|
||||
void validation_message::clear()
|
||||
{
|
||||
m_message.clear();
|
||||
}
|
||||
|
||||
bool validation_message::has_error() const
|
||||
{
|
||||
return !m_message.empty();
|
||||
}
|
||||
|
||||
const std::string& validation_message::get() const
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
|
||||
void validation_message::render(const core::palette& palette)
|
||||
{
|
||||
if (m_message.empty())
|
||||
return;
|
||||
|
||||
ImGui::Spacing();
|
||||
ImVec4 error_color = palette_color(palette, "error", "accent");
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, error_color);
|
||||
ImGui::TextWrapped("%s", m_message.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
25
src/gui/widgets/validation_message.hpp
Normal file
25
src/gui/widgets/validation_message.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef CLRSYNC_GUI_WIDGETS_VALIDATION_MESSAGE_HPP
|
||||
#define CLRSYNC_GUI_WIDGETS_VALIDATION_MESSAGE_HPP
|
||||
|
||||
#include "core/palette/palette.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace clrsync::gui::widgets
|
||||
{
|
||||
|
||||
class validation_message
|
||||
{
|
||||
public:
|
||||
void set(const std::string& message);
|
||||
void clear();
|
||||
bool has_error() const;
|
||||
const std::string& get() const;
|
||||
void render(const core::palette& palette);
|
||||
|
||||
private:
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
} // namespace clrsync::gui::widgets
|
||||
|
||||
#endif // CLRSYNC_GUI_WIDGETS_VALIDATION_MESSAGE_HPP
|
||||
Reference in New Issue
Block a user