Added text/highlight colors to text edit tool

This commit is contained in:
Yousef Khadadeh 2026-04-04 19:53:45 +03:00
parent 0087048a02
commit c85fdbb61f
11 changed files with 136 additions and 100 deletions

View file

@ -23,7 +23,7 @@ void BrushEditTool::edit_gui() {
gui.new_id("edit tool brush", [&] {
text_label_centered(gui, "Edit Brush Stroke");
left_to_right_line_layout(gui, [&]() {
t.color_button_right("Outline Color", &a.d.color, commit_update_func);
t.color_button_right("Outline Color", &a.d.color, { .onChange = commit_update_func });
text_label(gui, "Outline Color");
});
});

View file

@ -38,14 +38,14 @@ void EllipseDrawEditTool::edit_gui() {
}, commit_update_and_layout_func);
if(a.d.fillStrokeMode == 0 || a.d.fillStrokeMode == 2) {
left_to_right_line_layout(gui, [&] {
t.color_button_right("Fill color button", &a.d.fillColor, commit_update_func);
t.color_button_right("Fill color button", &a.d.fillColor, { .onChange = commit_update_func });
text_label(gui, "Fill Color");
});
}
if(a.d.fillStrokeMode == 1 || a.d.fillStrokeMode == 2) {
slider_scalar_field(gui, "relstrokewidth", "Outline Size", &a.d.strokeWidth, 3.0f, 40.0f, {.onEdit = commit_update_func});
left_to_right_line_layout(gui, [&] {
t.color_button_right("Outline color button", &a.d.strokeColor, commit_update_func);
t.color_button_right("Outline color button", &a.d.strokeColor, { .onChange = commit_update_func });
text_label(gui, "Outline Color");
});
}

View file

@ -36,14 +36,14 @@ void RectDrawEditTool::edit_gui() {
}, commit_update_and_layout_func);
if(a.d.fillStrokeMode == 0 || a.d.fillStrokeMode == 2) {
left_to_right_line_layout(gui, [&] {
t.color_button_right("Fill color button", &a.d.fillColor, commit_update_func);
t.color_button_right("Fill color button", &a.d.fillColor, { .onChange = commit_update_func });
text_label(gui, "Fill Color");
});
}
if(a.d.fillStrokeMode == 1 || a.d.fillStrokeMode == 2) {
slider_scalar_field(gui, "relstrokewidth", "Outline Size", &a.d.strokeWidth, 3.0f, 40.0f, { .onEdit = commit_update_func });
left_to_right_line_layout(gui, [&] {
t.color_button_right("Outline color button", &a.d.strokeColor, commit_update_func);
t.color_button_right("Outline color button", &a.d.strokeColor, { .onChange = commit_update_func });
text_label(gui, "Outline Color");
});
}

View file

@ -148,62 +148,50 @@ void TextBoxEditTool::edit_gui() {
.onDeselect = [&] { release_undo_data("Font Size"); }
});
//// Text color
//gui.left_to_right_line_layout([&]() {
// if(gui.color_button_big("Text Color", &newTextColor, &newTextColor == t.colorRight))
// t.color_selector_right(&newTextColor == t.colorRight ? nullptr : &newTextColor);
// gui.text_label("Text Color");
//});
//if(&newTextColor == t.colorRight)
// hold_undo_data("Text Color", a);
//else
// release_undo_data("Text Color");
//if((&newTextColor == t.colorRight) && t.isUpdatingColorRight) {
// currentMods[TextStyleModifier::ModifierType::COLOR] = std::make_shared<ColorTextStyleModifier>(newTextColor);
// a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::COLOR]);
//}
//// Highlight color
//gui.left_to_right_line_layout([&]() {
// if(gui.color_button_big("Highlight Color", &newHighlightColor, &newHighlightColor == t.colorRight)) {
// if(newHighlightColor.w() == 0.0f) { // Make highlight appear when the button is pressed
// newHighlightColor.w() = 1.0f;
// currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR] = std::make_shared<HighlightColorTextStyleModifier>(newHighlightColor);
// add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR]);});
// }
// t.color_selector_right(&newHighlightColor == t.colorRight ? nullptr : &newHighlightColor);
// }
// if(newHighlightColor.w() != 0.0f) {
// if(gui.svg_icon_button("Remove Highlight Color", "data/icons/close.svg")) {
// newHighlightColor = {0.0f, 0.0f, 0.0f, 0.0f};
// currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR] = std::make_shared<HighlightColorTextStyleModifier>(newHighlightColor);
// add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR]);});
// }
// }
// gui.text_label("Highlight Color");
//});
//if(&newHighlightColor == t.colorRight)
// hold_undo_data("Highlight Color", a);
//else
// release_undo_data("Highlight Color");
//if((&newHighlightColor == t.colorRight) && t.isUpdatingColorRight) {
// currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR] = std::make_shared<HighlightColorTextStyleModifier>(newHighlightColor);
// a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR]);
//}
left_to_right_line_layout(gui, [&]() {
t.color_button_right("Text Color", &newTextColor, {
.onChange = [&] {
currentMods[TextStyleModifier::ModifierType::COLOR] = std::make_shared<ColorTextStyleModifier>(newTextColor);
a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::COLOR]);
commit_update_func();
},
.onSelect = [&] { hold_undo_data("Text Color", a); },
.onDeselect = [&] { release_undo_data("Text Color"); }
});
text_label(gui, "Text Color");
});
left_to_right_line_layout(gui, [&]() {
t.color_button_right("Highlight Color", &newHighlightColor, {
.onSelectorButtonClick = [&] {
if(newHighlightColor.w() == 0.0f) {
newHighlightColor.w() = 1.0f;
currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR] = std::make_shared<HighlightColorTextStyleModifier>(newHighlightColor);
add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR]);});
commit_update_and_layout_func();
}
},
.onChange = [&] {
currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR] = std::make_shared<HighlightColorTextStyleModifier>(newHighlightColor);
a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR]);
commit_update_and_layout_func();
},
.onSelect = [&] { hold_undo_data("Highlight Color", a); },
.onDeselect = [&] { release_undo_data("Highlight Color"); }
});
if(newHighlightColor.w() != 0.0f) {
svg_icon_button(gui, "Remove Highlight Color", "data/icons/close.svg", {
.onClick = [&] {
newHighlightColor = {0.0f, 0.0f, 0.0f, 0.0f};
currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR] = std::make_shared<HighlightColorTextStyleModifier>(newHighlightColor);
add_undo_if_selecting_area(a, [&]() {a.textBox->set_text_style_modifier_between(a.cursor->selectionBeginPos, a.cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::HIGHLIGHT_COLOR]);});
commit_update_and_layout_func();
}
});
}
text_label(gui, "Highlight Color");
});
});
//if(a.textBox->inputChangedTextBox)
// set_styles_at_selection(a);
//// NOTE: There should be a system that periodically sends updates even if no changes are made, since unreliable channels can drop update data
//bool oldInputChangedTextBox = a.textBox->inputChangedTextBox;
//a.textBox->inputChangedTextBox = false;
//return oldInputChangedTextBox;
}
void TextBoxEditTool::input_paste_callback(const CustomEvents::PasteEventData& paste) {

View file

@ -8,6 +8,8 @@ namespace GUIStuff { namespace ElementHelpers {
struct ColorPickerItemsOptions {
bool hasAlpha = true;
std::function<void()> onEdit;
std::function<void()> onSelect;
std::function<void()> onDeselect;
};
template <typename T> void color_picker_items(GUIManager& gui, const char* id, T* val, const ColorPickerItemsOptions& options = {}) {
@ -16,22 +18,22 @@ template <typename T> void color_picker_items(GUIManager& gui, const char* id, T
if(oE) oE();
gui.set_to_layout();
};
gui.element<ColorPicker<T>>("c", val, options.hasAlpha, fullOnEdit);
gui.element<ColorPicker<T>>("c", val, options.hasAlpha, ColorPickerData{ .onChange = fullOnEdit, .onHold = options.onSelect, .onRelease = options.onDeselect });
left_to_right_line_layout(gui, [&]() {
text_label(gui, "R");
input_color_component_255(gui, "r", &(*val)[0], { .onEdit = fullOnEdit });
input_color_component_255(gui, "r", &(*val)[0], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
text_label(gui, "G");
input_color_component_255(gui, "g", &(*val)[1], { .onEdit = fullOnEdit });
input_color_component_255(gui, "g", &(*val)[1], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
text_label(gui, "B");
input_color_component_255(gui, "b", &(*val)[2], { .onEdit = fullOnEdit });
input_color_component_255(gui, "b", &(*val)[2], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
if(options.hasAlpha) {
text_label(gui, "A");
input_color_component_255(gui, "a", &(*val)[3], { .onEdit = fullOnEdit });
input_color_component_255(gui, "a", &(*val)[3], { .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
}
});
left_to_right_line_layout(gui, [&]() {
text_label(gui, "Hex");
input_color_hex(gui, "h", val, { .hasAlpha = options.hasAlpha, .onEdit = fullOnEdit });
input_color_hex(gui, "h", val, { .hasAlpha = options.hasAlpha, .onEdit = fullOnEdit, .onSelect = options.onSelect, .onDeselect = options.onDeselect });
});
});
}

View file

@ -58,15 +58,21 @@ namespace ColorPickerShaders {
sk_sp<SkShader> get_alpha_bar_shader(const Vector3f& mainColor, float horizontalResolution);
}
struct ColorPickerData {
std::function<void()> onChange;
std::function<void()> onHold;
std::function<void()> onRelease;
};
template <typename T> class ColorPicker : public Element {
public:
ColorPicker(GUIManager& gui);
void layout(const Clay_ElementId& id, T* data, bool selectAlpha, const std::function<void()>& onChange);
void layout(const Clay_ElementId& id, T* data, bool selectAlpha, const ColorPickerData& config);
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA);
void input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) override;
void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
private:
void update_color_picker_pos(const Vector2f& p);
void update_color_picker_pos(const Vector2f& p, bool justHeld);
float get_sv_selection_area_size();
Vector2f get_hue_bar_pos();
Vector2f get_hue_bar_dim();
@ -80,7 +86,7 @@ template <typename T> class ColorPicker : public Element {
T* data;
std::optional<T> oldData;
Vector3f savedHsv; // We save the HSV so that conversion doesnt ruin the UI
std::function<void()> onChange;
ColorPickerData config;
bool selectAlpha = false;
enum class HeldBar {
NONE,

View file

@ -6,14 +6,14 @@ namespace GUIStuff {
template <typename T> ColorPicker<T>::ColorPicker(GUIManager& gui): Element(gui) {}
template <typename T> void ColorPicker<T>::layout(const Clay_ElementId& id, T* data, bool selectAlpha, const std::function<void()>& onChange) {
template <typename T> void ColorPicker<T>::layout(const Clay_ElementId& id, T* data, bool selectAlpha, const ColorPickerData& config) {
this->data = data;
if(!oldData.has_value() || oldData.value() != *data) {
savedHsv = rgb_to_hsv<Vector3f, T>(*data);
oldData = *data;
}
this->selectAlpha = selectAlpha;
this->onChange = onChange;
this->config = config;
CLAY(id, {
.layout = {
@ -83,6 +83,7 @@ template <typename T> void ColorPicker<T>::clay_draw(SkCanvas* canvas, UpdateInp
}
template <typename T> void ColorPicker<T>::input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) {
HeldBar oldHeld = held;
held = HeldBar::NONE;
if(mouseHovering && button.button == InputManager::MouseButton::LEFT && button.down && boundingBox.has_value()) {
auto& bb = boundingBox.value();
@ -102,16 +103,21 @@ template <typename T> void ColorPicker<T>::input_mouse_button_callback(const Inp
}
}
}
update_color_picker_pos(button.pos);
if(oldHeld != HeldBar::NONE && held == HeldBar::NONE) {
gui.set_post_callback_func([&] {
if(config.onRelease) config.onRelease();
});
}
update_color_picker_pos(button.pos, true);
}
template <typename T> void ColorPicker<T>::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) {
update_color_picker_pos(motion.pos);
update_color_picker_pos(motion.pos, false);
}
template <typename T> void ColorPicker<T>::update_color_picker_pos(const Vector2f& p) {
template <typename T> void ColorPicker<T>::update_color_picker_pos(const Vector2f& p, bool justHeld) {
if(held != HeldBar::NONE && boundingBox.has_value()) {
gui.set_post_callback_func([&, p]() {
gui.set_post_callback_func([&, p, justHeld]() {
auto& bb = boundingBox.value();
switch(held) {
case HeldBar::NONE:
@ -122,7 +128,8 @@ template <typename T> void ColorPicker<T>::update_color_picker_pos(const Vector2
savedHsv.y() = newSv.x();
savedHsv.z() = 1.0f - newSv.y();
set_hsv(savedHsv);
if(onChange) onChange();
if(justHeld && config.onHold) config.onHold();
if(config.onChange) config.onChange();
break;
}
case HeldBar::HUE_HELD: {
@ -130,7 +137,8 @@ template <typename T> void ColorPicker<T>::update_color_picker_pos(const Vector2
Vector2f hueDim = get_hue_bar_dim();
savedHsv.x() = (1.0f - std::clamp((p.y() - huePos.y()) / hueDim.y(), 0.0f, 1.0f)) * 360.0f;
set_hsv(savedHsv);
if(onChange) onChange();
if(justHeld && config.onHold) config.onHold();
if(config.onChange) config.onChange();
break;
}
case HeldBar::ALPHA_HELD: {
@ -138,7 +146,8 @@ template <typename T> void ColorPicker<T>::update_color_picker_pos(const Vector2
Vector2f alphaDim = get_alpha_bar_dim();
(*data)[3] = std::clamp((p.x() - alphaPos.x()) / alphaDim.x(), 0.0f, 1.0f);
oldData = *data;
if(onChange) onChange();
if(justHeld && config.onHold) config.onHold();
if(config.onChange) config.onChange();
break;
}
}

View file

@ -6,6 +6,8 @@ namespace GUIStuff {
struct ColorPickerButtonData {
bool hasAlpha = true;
std::function<void()> onEdit;
std::function<void()> onSelect;
std::function<void()> onDeselect;
};
template <typename T> class ColorPickerButton : public Element {

View file

@ -22,7 +22,9 @@ template <typename T> void ColorPickerButton<T>::layout(const Clay_ElementId& id
ElementHelpers::top_to_bottom_window_popup_layout(gui, "Color Picker", CLAY_SIZING_FIT(300), CLAY_SIZING_FIT(0), [&, val, data = data](LayoutElement*) {
ElementHelpers::color_picker_items(gui, "c", val, {
.hasAlpha = data.hasAlpha,
.onEdit = data.onEdit
.onEdit = data.onEdit,
.onSelect = data.onSelect,
.onDeselect = data.onDeselect
});
}, {
.mouseButton = [&](LayoutElement* l, const InputManager::MouseButtonCallbackArgs& button) {

View file

@ -160,40 +160,50 @@ std::filesystem::path& Toolbar::file_selector_path() {
return filePicker.currentSearchPath;
}
void Toolbar::color_button_left(const char* id, Vector4f* color, const std::function<void()>& onChange) {
void Toolbar::color_button_left(const char* id, Vector4f* color, const ColorSelectorButtonData& colorSelectorData) {
auto& gui = main.g.gui;
color_button(gui, id, color, {
.isSelected = colorLeft == color,
.onClick = [&, onChange, color] {
color_selector_left(color, onChange);
.onClick = [&, colorSelectorData, color] {
if(colorSelectorData.onSelectorButtonClick) colorSelectorData.onSelectorButtonClick();
color_selector_left(color, {
.onChange = colorSelectorData.onChange,
.onSelect = colorSelectorData.onSelect,
.onDeselect = colorSelectorData.onDeselect,
});
}
});
}
void Toolbar::color_button_right(const char* id, Vector4f* color, const std::function<void()>& onChange) {
void Toolbar::color_button_right(const char* id, Vector4f* color, const ColorSelectorButtonData& colorSelectorData) {
auto& gui = main.g.gui;
color_button(gui, id, color, {
.isSelected = colorRight == color,
.onClick = [&, onChange, color] {
color_selector_right(color, onChange);
.onClick = [&, colorSelectorData, color] {
if(colorSelectorData.onSelectorButtonClick) colorSelectorData.onSelectorButtonClick();
color_selector_right(color, {
.onChange = colorSelectorData.onChange,
.onSelect = colorSelectorData.onSelect,
.onDeselect = colorSelectorData.onDeselect,
});
}
});
}
void Toolbar::color_selector_left(Vector4f* color, const std::function<void()>& onChange) {
void Toolbar::color_selector_left(Vector4f* color, const ColorSelectorData& colorSelectorData) {
if(colorLeft != color) {
colorLeftJustEnabled = color;
onColorLeftChange = onChange;
colorLeftData = colorSelectorData;
}
else
colorLeftJustDisabled = true;
main.g.gui.set_to_layout();
}
void Toolbar::color_selector_right(Vector4f* color, const std::function<void()>& onChange) {
void Toolbar::color_selector_right(Vector4f* color, const ColorSelectorData& colorSelectorData) {
if(colorRight != color) {
colorRightJustEnabled = color;
onColorRightChange = onChange;
colorRightData = colorSelectorData;
}
else
colorRightJustDisabled = true;
@ -1198,21 +1208,21 @@ void Toolbar::drawing_program_gui() {
colorRight = nullptr;
if(colorLeft)
color_picker_window("Drawing program gui color picker left", &colorLeft, &colorLeftJustDisabled, onColorLeftChange);
color_picker_window("Drawing program gui color picker left", &colorLeft, &colorLeftJustDisabled, colorLeftData);
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}
}
}) {}
if(colorRight)
color_picker_window("Drawing program gui color picker right", &colorRight, &colorRightJustDisabled, onColorRightChange);
color_picker_window("Drawing program gui color picker right", &colorRight, &colorRightJustDisabled, colorRightData);
main.world->drawProg.tool_options_gui();
main.world->drawProg.right_click_popup_gui();
}
}
void Toolbar::color_picker_window(const char* id, Vector4f** color, bool* colorJustDisabled, const std::function<void()>& onChange) {
void Toolbar::color_picker_window(const char* id, Vector4f** color, bool* colorJustDisabled, const ColorSelectorData& colorSelectorData) {
auto& gui = main.g.gui;
CLAY_AUTO_ID({
@ -1233,9 +1243,15 @@ void Toolbar::color_picker_window(const char* id, Vector4f** color, bool* colorJ
.cornerRadius = CLAY_CORNER_RADIUS(gui.io.theme->windowCorners1)
}) {
color_picker_items(gui, "colorpicker", *color, {
.onEdit = onChange
.onEdit = colorSelectorData.onChange,
.onSelect = colorSelectorData.onSelect,
.onDeselect = colorSelectorData.onDeselect,
});
color_palette("colorpickerpalette", *color, [colorSelectorData] {
if(colorSelectorData.onSelect) colorSelectorData.onSelect();
if(colorSelectorData.onChange) colorSelectorData.onChange();
if(colorSelectorData.onDeselect) colorSelectorData.onDeselect();
});
color_palette("colorpickerpalette", *color, onChange);
}
}, LayoutElement::Callbacks{
.mouseButton = [&, colorJustDisabled](LayoutElement* l, const InputManager::MouseButtonCallbackArgs& button) {

View file

@ -42,10 +42,21 @@ class Toolbar {
Toolbar(MainProgram& initMain);
void layout_run();
void color_selector_left(Vector4f* color, const std::function<void()>& onChange = nullptr);
void color_selector_right(Vector4f* color, const std::function<void()>& onChange = nullptr);
void color_button_left(const char* id, Vector4f* color, const std::function<void()>& onChange = nullptr);
void color_button_right(const char* id, Vector4f* color, const std::function<void()>& onChange = nullptr);
struct ColorSelectorData {
std::function<void()> onChange;
std::function<void()> onSelect;
std::function<void()> onDeselect;
};
struct ColorSelectorButtonData {
std::function<void()> onSelectorButtonClick;
std::function<void()> onChange;
std::function<void()> onSelect;
std::function<void()> onDeselect;
};
void color_selector_left(Vector4f* color, const ColorSelectorData& colorSelectorData = {});
void color_selector_right(Vector4f* color, const ColorSelectorData& colorSelectorData = {});
void color_button_left(const char* id, Vector4f* color, const ColorSelectorButtonData& colorSelectorData = {});
void color_button_right(const char* id, Vector4f* color, const ColorSelectorButtonData& colorSelectorData = {});
void paint_popup(Vector2f popupPos);
@ -62,12 +73,12 @@ class Toolbar {
Vector4f* colorLeft = nullptr;
bool colorLeftJustDisabled = false;
Vector4f* colorLeftJustEnabled = nullptr;
std::function<void()> onColorLeftChange;
ColorSelectorData colorLeftData;
Vector4f* colorRight = nullptr;
bool colorRightJustDisabled = false;
Vector4f* colorRightJustEnabled = nullptr;
std::function<void()> onColorRightChange;
ColorSelectorData colorRightData;
bool app_close_requested();
private:
@ -89,7 +100,7 @@ class Toolbar {
void file_picker_gui_done();
void file_picker_gui();
void performance_metrics();
void color_picker_window(const char* id, Vector4f** color, bool* colorJustDisabled, const std::function<void()>& onChange);
void color_picker_window(const char* id, Vector4f** color, bool* colorJustDisabled, const ColorSelectorData& colorSelectorData);
void color_palette(const char* id, Vector4f* color, const std::function<void()>& onChange);
void open_world_file(bool isClient, const std::string& netSource, const std::string& serverLocalID);
void load_default_palette();