Separated color picker element into 3 different GUI elements

This commit is contained in:
Yousef Khadadeh 2026-05-02 22:00:00 +03:00
parent 515b591ee8
commit b5d4c38024
16 changed files with 599 additions and 219 deletions

View file

@ -0,0 +1,73 @@
#include "AlphaSlider.hpp"
#include "../GUIManager.hpp"
#include "ColorPicker.hpp"
namespace GUIStuff {
AlphaSlider::AlphaSlider(GUIManager& gui): Element(gui) {}
void AlphaSlider::layout(const Clay_ElementId& id, const AlphaSliderData& opts) {
o = opts;
update();
CLAY(id, {
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}},
.custom = {.customData = this}
}) {
}
}
void AlphaSlider::update() {
if(currentG != *o.g || currentB != *o.b || currentR != *o.r || currentAlpha != *o.alpha) {
currentR = *o.r;
currentG = *o.g;
currentB = *o.b;
currentAlpha = *o.alpha;
gui.invalidate_draw_element(this);
}
}
void AlphaSlider::input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) {
if(mouseHovering && button.down && button.button == InputManager::MouseButton::LEFT) {
if(o.onHold) o.onHold();
update_alpha(button.pos);
if(o.onChange) o.onChange();
isHeld = true;
}
else if(isHeld && !button.down && button.button == InputManager::MouseButton::LEFT) {
if(o.onRelease) o.onRelease();
isHeld = false;
}
}
void AlphaSlider::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) {
if(isHeld) {
update_alpha(motion.pos);
if(o.onChange) o.onChange();
}
}
void AlphaSlider::update_alpha(Vector2f p) {
*o.alpha = std::clamp((p.x() - boundingBox.value().min.x()) / boundingBox.value().width(), 0.0f, 1.0f);
}
void AlphaSlider::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {
auto& bb = boundingBox.value();
canvas->save();
canvas->translate(bb.min.x(), bb.min.y());
canvas->clipRect(SkRect::MakeXYWH(0.0f, 0.0f, bb.width(), bb.height()), skiaAA);
SkPaint alphaBarPaint;
alphaBarPaint.setShader(ColorPickerShaders::get_alpha_bar_shader({currentR, currentG, currentB}, bb.width()));
canvas->drawPaint(alphaBarPaint);
SkPaint selectionLinePaint({1.0f, 1.0f, 1.0f, 1.0f});
selectionLinePaint.setAntiAlias(skiaAA);
selectionLinePaint.setStrokeWidth(1.0f);
canvas->drawLine(currentAlpha * bb.width(), 0.0f, currentAlpha * bb.width(), bb.height(), selectionLinePaint);
canvas->restore();
}
}

View file

@ -0,0 +1,36 @@
#pragma once
#include "Element.hpp"
namespace GUIStuff {
struct AlphaSliderData {
float* alpha = nullptr;
float* r = nullptr;
float* g = nullptr;
float* b = nullptr;
std::function<void()> onChange;
std::function<void()> onHold;
std::function<void()> onRelease;
};
class AlphaSlider : public Element {
public:
AlphaSlider(GUIManager& gui);
void layout(const Clay_ElementId& id, const AlphaSliderData& opts);
virtual void update() override;
virtual void input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) override;
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) override;
private:
void update_alpha(Vector2f p);
AlphaSliderData o;
float currentAlpha = -1.0f;
float currentR = -1.0f;
float currentG = -1.0f;
float currentB = -1.0f;
bool isHeld = false;
};
}

View file

@ -3,7 +3,6 @@
#include <include/core/SkColor.h>
#include <include/core/SkPath.h>
#include <include/effects/SkRuntimeEffect.h>
#include <iostream>
#include <Helpers/SCollision.hpp>
#include <Helpers/HsvRgb.hpp>
@ -67,44 +66,17 @@ struct ColorPickerData {
template <typename T> class ColorPicker : public Element {
public:
ColorPicker(GUIManager& gui);
void layout(const Clay_ElementId& id, T* data, bool selectAlpha, const ColorPickerData& config);
virtual void update() override;
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) override;
void input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) override;
void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
void layout(const Clay_ElementId& id, T* data, bool selectAlpha, const ColorPickerData& conf = {});
private:
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();
Vector2f get_alpha_bar_pos();
Vector2f get_alpha_bar_dim();
void set_hsv(const Vector3f& hsv);
void set_rgb_from_hsv(const Vector3f& hsv);
static constexpr float BAR_WIDTH = 30.0;
static constexpr float BAR_GAP = 5.0;
struct DisplayData {
Vector3f savedHsv = {0.0f, 0.0f, 0.0f};
float savedAlpha = 0.0f;
bool operator!=(const DisplayData&) const = default;
bool operator==(const DisplayData&) const = default;
};
DisplayData dd;
DisplayData oldDD;
static constexpr uint16_t BAR_GAP = 5;
Vector3f savedHsv = {0.0f, 0.0f, 0.0f};
T* data;
std::optional<T> oldData;
ColorPickerData config;
bool selectAlpha = false;
enum class HeldBar {
NONE,
SV_HELD,
HUE_HELD,
ALPHA_HELD
};
HeldBar held = HeldBar::NONE;
};
}

View file

@ -1,192 +1,116 @@
#pragma once
#include "ColorPicker.decl.hpp"
#include "../GUIManager.hpp"
#include "Helpers/ConvertVec.hpp"
#include "SaturationValueSquare.hpp"
#include "HueVerticalSlider.hpp"
#include "AlphaSlider.hpp"
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 ColorPickerData& config) {
template <typename T> void ColorPicker<T>::layout(const Clay_ElementId& id, T* data, bool selectAlpha, const ColorPickerData& conf) {
this->data = data;
if(!oldData.has_value() || oldData.value() != *data) {
dd.savedHsv = rgb_to_hsv<Vector3f, T>(*data);
savedHsv = rgb_to_hsv<Vector3f, T>(*data);
oldData = *data;
}
if(selectAlpha)
dd.savedAlpha = (*data)[3];
this->selectAlpha = selectAlpha;
this->config = config;
config = conf;
CLAY(id, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }
},
.aspectRatio = {1.0f},
.custom = { .customData = this }
}) {}
}
template <typename T> void ColorPicker<T>::update() {
if(dd != oldDD) {
gui.invalidate_draw_element(this);
oldDD = dd;
}
}
template <typename T> void ColorPicker<T>::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {
auto& bb = boundingBox.value();
canvas->save();
canvas->translate(bb.min.x(), bb.min.y());
float svSelectionAreaSize = get_sv_selection_area_size();
canvas->scale(svSelectionAreaSize, svSelectionAreaSize);
canvas->clipRect(SkRect::MakeXYWH(0.0f, 0.0f, 1.0f, 1.0f), skiaAA);
SkPaint svSelectionAreaPaint;
svSelectionAreaPaint.setShader(ColorPickerShaders::get_sv_selection_shader(dd.savedHsv.x() / 360.0f));
canvas->drawPaint(svSelectionAreaPaint);
SkPaint selectionLinePaint({1.0f, 1.0f, 1.0f, 1.0f});
selectionLinePaint.setAntiAlias(skiaAA);
selectionLinePaint.setStrokeWidth(2.0f / svSelectionAreaSize);
canvas->drawLine(0.0f, 1.0f - dd.savedHsv.z(), 1.0f, 1.0f - dd.savedHsv.z(), selectionLinePaint);
canvas->drawLine(dd.savedHsv.y(), 0.0f, dd.savedHsv.y(), 1.0f, selectionLinePaint);
canvas->restore();
float normalizedHue = dd.savedHsv.x() / 360.0f;
Vector2f hueBarPos = get_hue_bar_pos();
Vector2f hueBarDim = get_hue_bar_dim();
canvas->save();
canvas->translate(hueBarPos.x(), hueBarPos.y());
canvas->scale(hueBarDim.x(), hueBarDim.y());
canvas->clipRect(SkRect::MakeXYWH(0.0f, 0.0f, 1.0f, 1.0f), skiaAA);
SkPaint hueBarPaint;
hueBarPaint.setShader(ColorPickerShaders::get_hue_shader());
canvas->drawPaint(hueBarPaint);
canvas->drawLine(0.0f, 1.0f - normalizedHue, 1.0f, 1.0f - normalizedHue, selectionLinePaint);
canvas->restore();
Vector2f alphaBarPos = get_alpha_bar_pos();
Vector2f alphaBarDim = get_alpha_bar_dim();
canvas->save();
canvas->translate(alphaBarPos.x(), alphaBarPos.y());
canvas->clipRect(SkRect::MakeXYWH(0.0f, 0.0f, alphaBarDim.x(), alphaBarDim.y()), skiaAA);
SkPaint alphaBarPaint;
if(selectAlpha)
alphaBarPaint.setShader(ColorPickerShaders::get_alpha_bar_shader({(*data)[0], (*data)[1], (*data)[2]}, alphaBarDim.x()));
else
alphaBarPaint.setColor4f(SkColor4f{(*data)[0], (*data)[1], (*data)[2], 1.0f});
canvas->drawPaint(alphaBarPaint);
auto onChange = [&] {
set_rgb_from_hsv(savedHsv);
if(config.onChange) config.onChange();
};
if(selectAlpha) {
canvas->scale(alphaBarDim.x(), alphaBarDim.y());
canvas->drawLine((*data)[3], 0.0f, (*data)[3], 1.0f, selectionLinePaint);
}
canvas->restore();
}
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();
float svSelectionAreaSize = get_sv_selection_area_size();
if(SCollision::collide(SCollision::AABB<float>(bb.min, bb.min + Vector2f{svSelectionAreaSize, svSelectionAreaSize}), button.pos))
held = HeldBar::SV_HELD;
else {
Vector2f hueBarPos = get_hue_bar_pos();
Vector2f hueBarDim = get_hue_bar_dim();
if(SCollision::collide(SCollision::AABB<float>(hueBarPos, hueBarPos + hueBarDim), button.pos))
held = HeldBar::HUE_HELD;
else if(selectAlpha) {
Vector2f alphaBarPos = get_alpha_bar_pos();
Vector2f alphaBarDim = get_alpha_bar_dim();
if(SCollision::collide(SCollision::AABB<float>(alphaBarPos, alphaBarPos + alphaBarDim), button.pos))
held = HeldBar::ALPHA_HELD;
CLAY(id, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) },
.childGap = BAR_GAP,
.layoutDirection = CLAY_TOP_TO_BOTTOM
}
}) {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
.childGap = BAR_GAP,
.layoutDirection = CLAY_LEFT_TO_RIGHT
},
}) {
gui.element<SaturationValueSquare>("sv square", SaturationValueSquareData{
.saturation = &savedHsv.y(),
.value = &savedHsv.z(),
.hue = &savedHsv.x(),
.onChange = onChange,
.onHold = config.onHold,
.onRelease = config.onRelease
});
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(BAR_WIDTH), .height = CLAY_SIZING_GROW(0)}
},
}) {
gui.element<HueVerticalSlider>("hue slider", HueVerticalSliderData{
.hue = &savedHsv.x(),
.onChange = onChange,
.onHold = config.onHold,
.onRelease = config.onRelease
});
}
}
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(BAR_WIDTH)}
},
}) {
gui.element<AlphaSlider>("alpha slider", AlphaSliderData{
.alpha = &(*data)[3],
.r = &(*data)[0],
.g = &(*data)[1],
.b = &(*data)[2],
.onChange = onChange,
.onHold = config.onHold,
.onRelease = config.onRelease
});
}
}
}
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, false);
}
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, justHeld]() {
auto& bb = boundingBox.value();
switch(held) {
case HeldBar::NONE:
break;
case HeldBar::SV_HELD: {
float svSelectionAreaSize = bb.width() - (BAR_GAP + BAR_WIDTH);
Vector2f newSv = cwise_vec_clamp<Vector2f>((p - bb.min) / svSelectionAreaSize, Vector2f{0.0f, 0.0f}, Vector2f{1.0f, 1.0f});
dd.savedHsv.y() = newSv.x();
dd.savedHsv.z() = 1.0f - newSv.y();
set_hsv(dd.savedHsv);
if(justHeld && config.onHold) config.onHold();
if(config.onChange) config.onChange();
break;
}
case HeldBar::HUE_HELD: {
Vector2f huePos = get_hue_bar_pos();
Vector2f hueDim = get_hue_bar_dim();
dd.savedHsv.x() = (1.0f - std::clamp((p.y() - huePos.y()) / hueDim.y(), 0.0f, 1.0f)) * 360.0f;
set_hsv(dd.savedHsv);
if(justHeld && config.onHold) config.onHold();
if(config.onChange) config.onChange();
break;
}
case HeldBar::ALPHA_HELD: {
Vector2f alphaPos = get_alpha_bar_pos();
Vector2f alphaDim = get_alpha_bar_dim();
(*data)[3] = std::clamp((p.x() - alphaPos.x()) / alphaDim.x(), 0.0f, 1.0f);
dd.savedAlpha = (*data)[3];
oldData = *data;
if(justHeld && config.onHold) config.onHold();
if(config.onChange) config.onChange();
break;
}
else {
CLAY(id, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)},
.childGap = BAR_GAP,
.layoutDirection = CLAY_LEFT_TO_RIGHT
},
}) {
gui.element<SaturationValueSquare>("sv square", SaturationValueSquareData{
.saturation = &savedHsv.y(),
.value = &savedHsv.z(),
.hue = &savedHsv.x(),
.onChange = onChange,
.onHold = config.onHold,
.onRelease = config.onRelease
});
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(BAR_WIDTH), .height = CLAY_SIZING_GROW(0)},
.layoutDirection = CLAY_LEFT_TO_RIGHT
},
}) {
gui.element<HueVerticalSlider>("hue slider", HueVerticalSliderData{
.hue = &savedHsv.x(),
.onChange = onChange,
.onHold = config.onHold,
.onRelease = config.onRelease
});
}
});
}
}
}
template <typename T> float ColorPicker<T>::get_sv_selection_area_size() {
return boundingBox.value().width() - (BAR_GAP + BAR_WIDTH);
}
template <typename T> Vector2f ColorPicker<T>::get_hue_bar_pos() {
return {boundingBox.value().min.x() + get_sv_selection_area_size() + BAR_GAP, boundingBox.value().min.y()};
}
template <typename T> Vector2f ColorPicker<T>::get_hue_bar_dim() {
return {BAR_WIDTH, get_sv_selection_area_size()};
}
template <typename T> Vector2f ColorPicker<T>::get_alpha_bar_pos() {
return {boundingBox.value().min.x(), boundingBox.value().min.y() + get_sv_selection_area_size() + BAR_GAP};
}
template <typename T> Vector2f ColorPicker<T>::get_alpha_bar_dim() {
return {boundingBox.value().width(), BAR_WIDTH};
}
template <typename T> void ColorPicker<T>::set_hsv(const Vector3f& hsv) {
template <typename T> void ColorPicker<T>::set_rgb_from_hsv(const Vector3f& hsv) {
Vector3f a = hsv_to_rgb<Vector3f>(hsv);
(*data)[0] = a.x();
(*data)[1] = a.y();

View file

@ -16,7 +16,6 @@ struct DropdownOptions {
template <typename T> class DropDown : public Element {
public:
static constexpr float ENTRY_HEIGHT = 27.0f;
static constexpr float DROPDOWN_OFFSET = 4.0f;
DropDown(GUIManager& gui): Element(gui) {}
@ -61,6 +60,7 @@ template <typename T> class DropDown : public Element {
if(isOpen && boundingBox.has_value()) {
auto& buttonBB = boundingBox.value();
float ENTRY_HEIGHT = buttonBB.height();
gui.set_z_index(gui.get_z_index() + 1, [&] {
gui.element<LayoutElement>("DROPDOWN", [&](LayoutElement* l, const Clay_ElementId& lId) {
float dropdownHeight = ENTRY_HEIGHT * selections.size() + DROPDOWN_OFFSET;

View file

@ -14,8 +14,12 @@ namespace GUIStuff {
void Element::input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) { }
void Element::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) { }
void Element::input_mouse_wheel_callback(const InputManager::MouseWheelCallbackArgs& wheel) { }
void Element::input_finger_touch_callback(const InputManager::FingerTouchCallbackArgs& touch) { }
void Element::input_finger_motion_callback(const InputManager::FingerMotionCallbackArgs& motion) { }
void Element::input_finger_touch_callback(const InputManager::FingerTouchCallbackArgs& touch) {
input_mouse_button_callback(InputManager::convert_finger_touch_to_mouse_button(touch));
}
void Element::input_finger_motion_callback(const InputManager::FingerMotionCallbackArgs& motion) {
input_mouse_motion_callback(InputManager::convert_finger_motion_to_mouse_motion(motion));
}
void Element::input_key_callback(const InputManager::KeyCallbackArgs& key) {}
void Element::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {}

View file

@ -0,0 +1,75 @@
#include "HueVerticalSlider.hpp"
#include "../GUIManager.hpp"
#include "ColorPicker.hpp"
namespace GUIStuff {
HueVerticalSlider::HueVerticalSlider(GUIManager& gui): Element(gui) {}
void HueVerticalSlider::layout(const Clay_ElementId& id, const HueVerticalSliderData& opts) {
o = opts;
update();
CLAY(id, {
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}},
.custom = {.customData = this}
}) {
}
}
void HueVerticalSlider::update() {
if(currentHue != *o.hue) {
currentHue = *o.hue;
gui.invalidate_draw_element(this);
}
}
void HueVerticalSlider::input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) {
if(mouseHovering && button.down && button.button == InputManager::MouseButton::LEFT) {
if(o.onHold) o.onHold();
update_hue(button.pos);
if(o.onChange) o.onChange();
isHeld = true;
}
else if(isHeld && !button.down && button.button == InputManager::MouseButton::LEFT) {
if(o.onRelease) o.onRelease();
isHeld = false;
}
}
void HueVerticalSlider::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) {
if(isHeld) {
update_hue(motion.pos);
if(o.onChange) o.onChange();
}
}
void HueVerticalSlider::update_hue(Vector2f p) {
*o.hue = (1.0f - std::clamp((p.y() - boundingBox.value().min.y()) / boundingBox.value().height(), 0.0f, 1.0f)) * 360.0f;
}
void HueVerticalSlider::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {
auto& bb = boundingBox.value();
float normalizedHue = currentHue / 360.0f;
canvas->save();
canvas->translate(bb.min.x(), bb.min.y());
canvas->scale(bb.width(), bb.height());
canvas->clipRect(SkRect::MakeXYWH(0.0f, 0.0f, 1.0f, 1.0f), skiaAA);
SkPaint hueBarPaint;
hueBarPaint.setShader(ColorPickerShaders::get_hue_shader());
canvas->drawPaint(hueBarPaint);
canvas->restore();
canvas->save();
canvas->translate(bb.min.x(), bb.min.y());
SkPaint selectionLinePaint({1.0f, 1.0f, 1.0f, 1.0f});
selectionLinePaint.setAntiAlias(skiaAA);
selectionLinePaint.setStrokeWidth(1.0f);
float hueY = bb.height() * (1.0f - normalizedHue);
canvas->drawLine(0.0f, hueY, bb.width(), hueY, selectionLinePaint);
canvas->restore();
}
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "Element.hpp"
namespace GUIStuff {
struct HueVerticalSliderData {
float* hue = nullptr;
std::function<void()> onChange;
std::function<void()> onHold;
std::function<void()> onRelease;
};
class HueVerticalSlider : public Element {
public:
HueVerticalSlider(GUIManager& gui);
void layout(const Clay_ElementId& id, const HueVerticalSliderData& opts);
virtual void update() override;
virtual void input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) override;
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) override;
private:
void update_hue(Vector2f p);
HueVerticalSliderData o;
float currentHue = -1.0f;
bool isHeld = false;
};
}

View file

@ -0,0 +1,82 @@
#include "SaturationValueSquare.hpp"
#include "../GUIManager.hpp"
#include "ColorPicker.hpp"
namespace GUIStuff {
SaturationValueSquare::SaturationValueSquare(GUIManager& gui): Element(gui) {}
void SaturationValueSquare::layout(const Clay_ElementId& id, const SaturationValueSquareData& opts) {
o = opts;
update();
CLAY(id, {
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}},
.custom = {.customData = this}
}) {
}
}
void SaturationValueSquare::update() {
if(currentHue != *o.hue || currentSaturation != *o.saturation || currentValue != *o.value) {
currentHue = *o.hue;
currentSaturation = *o.saturation;
currentValue = *o.value;
gui.invalidate_draw_element(this);
}
}
void SaturationValueSquare::input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) {
if(mouseHovering && button.down && button.button == InputManager::MouseButton::LEFT) {
if(o.onHold) o.onHold();
update_sv(button.pos);
if(o.onChange) o.onChange();
isHeld = true;
}
else if(isHeld && !button.down && button.button == InputManager::MouseButton::LEFT) {
if(o.onRelease) o.onRelease();
isHeld = false;
}
}
void SaturationValueSquare::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) {
if(isHeld) {
update_sv(motion.pos);
if(o.onChange) o.onChange();
}
}
void SaturationValueSquare::update_sv(Vector2f p) {
*o.saturation = std::clamp((p.x() - boundingBox.value().min.x()) / boundingBox.value().width(), 0.0f, 1.0f);
*o.value = 1.0f - std::clamp((p.y() - boundingBox.value().min.y()) / boundingBox.value().height(), 0.0f, 1.0f);
}
void SaturationValueSquare::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {
auto& bb = boundingBox.value();
canvas->save();
canvas->translate(bb.min.x(), bb.min.y());
canvas->scale(bb.width(), bb.height());
canvas->clipRect(SkRect::MakeXYWH(0.0f, 0.0f, 1.0f, 1.0f), skiaAA);
SkPaint svSelectionAreaPaint;
svSelectionAreaPaint.setShader(ColorPickerShaders::get_sv_selection_shader(currentHue / 360.0f));
canvas->drawPaint(svSelectionAreaPaint);
canvas->restore();
canvas->save();
canvas->translate(bb.min.x(), bb.min.y());
SkPaint selectionLinePaint({1.0f, 1.0f, 1.0f, 1.0f});
selectionLinePaint.setAntiAlias(skiaAA);
selectionLinePaint.setStrokeWidth(1.0f);
float valueY = bb.height() * (1.0f - currentValue);
canvas->drawLine(0.0f, valueY, bb.width(), valueY, selectionLinePaint);
float saturationX = bb.width() * currentSaturation;
canvas->drawLine(saturationX, 0.0f, saturationX, bb.height(), selectionLinePaint);
canvas->restore();
}
}

View file

@ -0,0 +1,34 @@
#pragma once
#include "Element.hpp"
namespace GUIStuff {
struct SaturationValueSquareData {
float* saturation = nullptr;
float* value = nullptr;
float* hue = nullptr;
std::function<void()> onChange;
std::function<void()> onHold;
std::function<void()> onRelease;
};
class SaturationValueSquare : public Element {
public:
SaturationValueSquare(GUIManager& gui);
void layout(const Clay_ElementId& id, const SaturationValueSquareData& opts);
virtual void update() override;
virtual void input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button) override;
virtual void input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion) override;
virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) override;
private:
void update_sv(Vector2f p);
SaturationValueSquareData o;
float currentHue = -1.0f;
float currentSaturation = -1.0f;
float currentValue = -1.0f;
bool isHeld = false;
};
}

View file

@ -624,6 +624,24 @@ void InputManager::backend_pen_axis_update(const SDL_PenAxisEvent& e) {
}
}
InputManager::MouseButtonCallbackArgs InputManager::convert_finger_touch_to_mouse_button(const FingerTouchCallbackArgs& touch) {
return {
.deviceType = InputManager::MouseDeviceType::TOUCH,
.button = InputManager::MouseButton::LEFT,
.down = touch.down,
.clicks = static_cast<uint8_t>(touch.fingerTapCount),
.pos = touch.pos
};
}
InputManager::MouseMotionCallbackArgs InputManager::convert_finger_motion_to_mouse_motion(const FingerMotionCallbackArgs& motion) {
return {
.deviceType = InputManager::MouseDeviceType::TOUCH,
.pos = motion.pos,
.move = motion.move
};
}
std::vector<Vector2f> InputManager::get_multiple_finger_positions() {
std::vector<Vector2f> pos;
for(const SDL_TouchFingerEvent& fingerEvent : touch.fingers)

View file

@ -395,6 +395,9 @@ struct InputManager {
size_t fingerDownCount;
};
static MouseButtonCallbackArgs convert_finger_touch_to_mouse_button(const FingerTouchCallbackArgs& touch);
static MouseMotionCallbackArgs convert_finger_motion_to_mouse_motion(const FingerMotionCallbackArgs& motion);
struct WindowResizeCallbackArgs {
Vector2i size;
SCollision::AABB<float> safeArea;

View file

@ -3,9 +3,12 @@
#include "DrawingProgramScreen.hpp"
#include "Helpers/ConvertVec.hpp"
#include "../GUIStuff/Elements/GridScrollArea.hpp"
#include "../GUIStuff/Elements/DropDown.hpp"
#include "../GUIStuff/Elements/ColorPicker.hpp"
#include "../GUIStuff/ElementHelpers/ButtonHelpers.hpp"
#include "../GUIStuff/ElementHelpers/LayoutHelpers.hpp"
#include "../GUIStuff/ElementHelpers/TextLabelHelpers.hpp"
#include "../GUIStuff/ElementHelpers/TextBoxHelpers.hpp"
#include "FileSelectScreen.hpp"
#include <Helpers/Logger.hpp>
@ -112,8 +115,10 @@ void PhoneDrawingProgramScreen::bottom_toolbar() {
}) {}
switch(settingsMenuPopup) {
case SettingsMenuPopup::NONE:
reset_color_picker_popup_data();
break;
case SettingsMenuPopup::SETTINGS:
reset_color_picker_popup_data();
tool_settings_popup();
break;
case SettingsMenuPopup::FG_COLOR:
@ -234,24 +239,25 @@ void PhoneDrawingProgramScreen::tool_settings_popup() {
});
}
void PhoneDrawingProgramScreen::reset_color_picker_popup_data() {
colorPickerPopupData.extraSettingsOpen = false;
colorPickerPopupData.addingPalette = false;
colorPickerPopupData.newPaletteStr.clear();
}
void PhoneDrawingProgramScreen::color_settings_popup(Vector4f* color) {
auto& gui = main.g.gui;
auto& palette = main.conf.palettes[paletteData.selectedPalette];
gui.element<LayoutElement>("color settings popup", [&] (LayoutElement*, const Clay_ElementId& lId) {
CLAY(lId, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)},
},
.backgroundColor = convert_vec4<Clay_Color>(gui.io.theme->backColor1),
.cornerRadius = CLAY_CORNER_RADIUS(gui.io.theme->windowCorners1)
}) {
gui.element<GridScrollArea>("color selector grid", GridScrollArea::Options{
.entryWidth = BIG_BUTTON_SIZE,
.childAlignmentX = CLAY_ALIGN_X_CENTER,
.entryHeight = BIG_BUTTON_SIZE,
.entryCount = palette.colors.size(),
.scrollbar = ScrollArea::ScrollbarType::NORMAL,
.elementContent = [&](size_t i) {
auto& palette = main.conf.palettes[colorPickerPopupData.selectedPalette];
auto paletteColorPickerGrid = [&] {
gui.element<GridScrollArea>("color selector grid", GridScrollArea::Options{
.entryWidth = BIG_BUTTON_SIZE,
.childAlignmentX = CLAY_ALIGN_X_CENTER,
.entryHeight = BIG_BUTTON_SIZE,
.entryCount = palette.colors.size() + 1,
.scrollbar = ScrollArea::ScrollbarType::NORMAL,
.elementContent = [&](size_t i) {
if(i < palette.colors.size()) {
auto newC = std::make_shared<Vector3f>(palette.colors[i].x(), palette.colors[i].y(), palette.colors[i].z());
color_button(gui, "c", newC.get(), {
.isSelected = newC->x() == color->x() && newC->y() == color->y() && newC->z() == color->z(),
@ -264,7 +270,124 @@ void PhoneDrawingProgramScreen::color_settings_popup(Vector4f* color) {
}
});
}
else {
svg_icon_button(gui, "extra color settings", "data/icons/RemixIcon/more-fill.svg", {
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
.isSelected = colorPickerPopupData.extraSettingsOpen,
.onClick = [&] {
colorPickerPopupData.extraSettingsOpen = !colorPickerPopupData.extraSettingsOpen;
}
});
}
}
});
};
auto paletteColorPickerExtras = [&] {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(200), .height = CLAY_SIZING_FIT(200)},
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER},
.layoutDirection = CLAY_LEFT_TO_RIGHT
}
}) {
gui.element<ColorPicker<Vector4f>>("color picker element", color, true);
}
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)},
.childGap = gui.io.theme->childGap1,
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER},
.layoutDirection = CLAY_LEFT_TO_RIGHT
}
}) {
std::vector<std::string> paletteNames;
for(auto& p : main.conf.palettes)
paletteNames.emplace_back(p.name);
gui.element<DropDown<size_t>>("paletteselector", &colorPickerPopupData.selectedPalette, paletteNames);
svg_icon_button(gui, "paletteadd", "data/icons/plus.svg", {
.onClick = [&] {
colorPickerPopupData.addingPalette = !colorPickerPopupData.addingPalette;
}
});
svg_icon_button(gui, "paletteremove", "data/icons/close.svg", {
.onClick = [&] {
main.conf.palettes.erase(main.conf.palettes.begin() + colorPickerPopupData.selectedPalette);
colorPickerPopupData.selectedPalette = 0;
}
});
}
if(colorPickerPopupData.addingPalette) {
input_text_field(gui, "paletteinputname", "Name", &colorPickerPopupData.newPaletteStr);
text_button(gui, "addpalettebutton", "Create", {
.wide = true,
.onClick = [&] {
if(!colorPickerPopupData.newPaletteStr.empty()) {
main.conf.palettes.emplace_back();
main.conf.palettes.back().name = colorPickerPopupData.newPaletteStr;
colorPickerPopupData.selectedPalette = main.conf.palettes.size() - 1;
colorPickerPopupData.addingPalette = false;
}
}
});
}
if(colorPickerPopupData.selectedPalette != 0) {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)},
.childGap = gui.io.theme->childGap1,
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER},
.layoutDirection = CLAY_LEFT_TO_RIGHT
}
}) {
svg_icon_button(gui, "addcolor", "data/icons/plus.svg", {
.onClick = [&, color] {
std::erase(palette.colors, Vector3f{color->x(), color->y(), color->z()});
palette.colors.emplace_back(color->x(), color->y(), color->z());
}
});
svg_icon_button(gui, "deletecolor", "data/icons/close.svg", {
.onClick = [&, color] {
std::erase(palette.colors, Vector3f{color->x(), color->y(), color->z()});
}
});
}
}
};
gui.element<LayoutElement>("color settings popup", [&] (LayoutElement* l, const Clay_ElementId& lId) {
CLAY(lId, {
.layout = {
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(0)},
.childGap = gui.io.theme->childGap1,
.childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER},
.layoutDirection = CLAY_LEFT_TO_RIGHT
},
.backgroundColor = convert_vec4<Clay_Color>(gui.io.theme->backColor1),
.cornerRadius = CLAY_CORNER_RADIUS(gui.io.theme->windowCorners1)
}) {
if(colorPickerPopupData.extraSettingsOpen) {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(0), .height = CLAY_SIZING_FIT(0)},
.childGap = gui.io.theme->childGap1,
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
}) {
paletteColorPickerGrid();
}
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIT(0), .height = CLAY_SIZING_FIT(0)},
.childGap = gui.io.theme->childGap1,
.layoutDirection = CLAY_TOP_TO_BOTTOM
},
}) {
paletteColorPickerExtras();
}
}
else
paletteColorPickerGrid();
}
});
}

View file

@ -24,9 +24,11 @@ class PhoneDrawingProgramScreen : public DrawingProgramScreen {
FG_COLOR,
BG_COLOR
} settingsMenuPopup = SettingsMenuPopup::NONE;
struct PaletteData {
struct ColorPickerPopupData {
bool extraSettingsOpen = false;
size_t selectedPalette = 0;
bool addingPalette = false;
std::string newPaletteStr;
} paletteData;
} colorPickerPopupData;
void reset_color_picker_popup_data();
};