436 lines
18 KiB
C++
436 lines
18 KiB
C++
#include "pch.h"
|
|
#include "log.h"
|
|
#include "node_panel_stroke.h"
|
|
#include "canvas.h"
|
|
#include "node_button.h"
|
|
#include "app.h"
|
|
|
|
Node* NodePanelStroke::clone_instantiate() const
|
|
{
|
|
return new NodePanelStroke();
|
|
}
|
|
|
|
void NodePanelStroke::clone_finalize(Node* dest) const
|
|
{
|
|
NodePanelStroke* n = static_cast<NodePanelStroke*>(dest);
|
|
n->init_controls();
|
|
}
|
|
|
|
void NodePanelStroke::init()
|
|
{
|
|
init_template("tpl-panel-stroke");
|
|
init_controls();
|
|
}
|
|
|
|
void NodePanelStroke::update_controls()
|
|
{
|
|
const auto& b = Canvas::I->m_current_brush;
|
|
m_tip_size->m_value.x = glm::pow(b->m_tip_size, 1.f/3.f);
|
|
m_tip_spacing->m_value.x = glm::pow(b->m_tip_spacing, 1.f/2.f);
|
|
m_tip_flow->m_value.x = glm::pow(b->m_tip_flow, 1.f/2.f);
|
|
m_tip_opacity->m_value.x = b->m_tip_opacity;
|
|
m_tip_angle->m_value.x = b->m_tip_angle;
|
|
m_tip_angle_delay->m_value.x = b->m_tip_angle_delay;
|
|
m_pattern_opacity->m_value.x = b->m_pattern_opacity;
|
|
m_tip_wet->m_value.x = b->m_tip_wet;
|
|
m_tip_noise->m_value.x = b->m_tip_noise;
|
|
m_jitter_scale->m_value.x = b->m_jitter_scale;
|
|
m_jitter_angle->m_value.x = b->m_jitter_angle;
|
|
m_jitter_spread->m_value.x = b->m_jitter_spread;
|
|
m_jitter_flow->m_value.x = b->m_jitter_flow;
|
|
m_jitter_hue->m_value.x = b->m_jitter_hue;
|
|
m_jitter_sat->m_value.x = b->m_jitter_sat;
|
|
m_jitter_val->m_value.x = b->m_jitter_val;
|
|
m_tip_angle_follow->checked = b->m_tip_angle_follow;
|
|
m_tip_flow_pressure->checked = b->m_tip_flow_pressure;
|
|
m_tip_size_pressure->checked = b->m_tip_size_pressure;
|
|
|
|
m_tip_invert->checked = b->m_tip_invert;
|
|
m_tip_flipx->checked = b->m_tip_flipx;
|
|
m_tip_flipy->checked = b->m_tip_flipy;
|
|
m_pattern_enabled->checked = b->m_pattern_enabled;
|
|
m_dual_enabled->checked = b->m_dual_enabled;
|
|
m_dual_scatter_axis->checked = b->m_dual_scatter_axis;
|
|
m_dual_invert->checked = b->m_dual_invert;
|
|
m_dual_flipx->checked = b->m_dual_flipx;
|
|
m_dual_flipy->checked = b->m_dual_flipy;
|
|
m_dual_randflip->checked = b->m_dual_randflip;
|
|
m_tip_randflipx->checked = b->m_tip_randflipx;
|
|
m_tip_randflipy->checked = b->m_tip_randflipy;
|
|
|
|
m_dual_size->m_value.x = glm::pow(b->m_dual_size, 1.f / 3.f);
|
|
m_dual_spacing->m_value.x = glm::pow(b->m_dual_spacing, 1.f / 2.f);
|
|
m_dual_flow->m_value.x = glm::pow(b->m_dual_flow, 1.f / 2.f);
|
|
m_dual_scatter->m_value.x = b->m_dual_scatter;
|
|
m_tip_aspect->m_value.x = b->m_tip_aspect;
|
|
m_dual_opacity->m_value.x = b->m_dual_opacity;
|
|
m_dual_rotate->m_value.x = b->m_dual_rotate;
|
|
|
|
m_pattern_eachsample->checked = b->m_pattern_eachsample;
|
|
m_pattern_invert->checked = b->m_pattern_invert;
|
|
m_pattern_flipx->checked = b->m_pattern_flipx;
|
|
m_pattern_flipy->checked = b->m_pattern_flipy;
|
|
m_pattern_scale->m_value.x = b->m_pattern_scale;
|
|
m_pattern_brightness->m_value.x = b->m_pattern_brightness;
|
|
m_pattern_contrast->m_value.x = b->m_pattern_contrast;
|
|
|
|
m_blend_mode->set_index(b->m_blend_mode);
|
|
m_dual_blend_mode->set_index(b->m_dual_blend_mode);
|
|
m_pattern_blend_mode->set_index(b->m_pattern_blend_mode);
|
|
|
|
m_preview->m_brush = b;
|
|
m_preview->draw_stroke();
|
|
}
|
|
|
|
void NodePanelStroke::init_controls()
|
|
{
|
|
m_brush_popup = std::make_shared<NodePanelBrush>();
|
|
m_brush_popup->m_manager = m_manager;
|
|
m_brush_popup->m_dir_name = "brushes";
|
|
m_brush_popup->init();
|
|
m_brush_popup->create();
|
|
m_brush_popup->loaded();
|
|
m_brush_popup->SetPositioning(YGPositionTypeAbsolute);
|
|
m_brush_popup->SetSize(300, 400);
|
|
m_brush_popup->m_mouse_ignore = false;
|
|
m_brush_popup->m_flood_events = true;
|
|
m_brush_popup->m_capture_children = false;
|
|
|
|
m_pattern_popup = std::make_shared<NodePanelBrush>();
|
|
m_pattern_popup->m_manager = m_manager;
|
|
m_pattern_popup->m_dir_name = "textures";
|
|
m_pattern_popup->init();
|
|
m_pattern_popup->create();
|
|
m_pattern_popup->loaded();
|
|
m_pattern_popup->SetPositioning(YGPositionTypeAbsolute);
|
|
m_pattern_popup->SetSize(300, 400);
|
|
m_pattern_popup->m_mouse_ignore = false;
|
|
m_pattern_popup->m_flood_events = true;
|
|
m_pattern_popup->m_capture_children = false;
|
|
|
|
m_presets_popup = std::make_shared<NodePanelBrushPreset>();
|
|
m_presets_popup->m_manager = m_manager;
|
|
m_presets_popup->init();
|
|
m_presets_popup->create();
|
|
m_presets_popup->loaded();
|
|
m_presets_popup->SetPositioning(YGPositionTypeAbsolute);
|
|
m_presets_popup->SetSize(200, 400);
|
|
m_presets_popup->m_mouse_ignore = false;
|
|
m_presets_popup->m_flood_events = true;
|
|
m_presets_popup->m_capture_children = false;
|
|
|
|
int br_idx = std::max(m_brush_popup->find_brush("Round-Hard"), 0);
|
|
|
|
// init main brush
|
|
auto b = std::make_shared<Brush>();
|
|
b->load_tip(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx));
|
|
b->load_dual(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx));
|
|
b->load_pattern(m_pattern_popup->get_texture_path(0), m_pattern_popup->get_thumb_path(0));
|
|
b->m_tip_size = .1f;
|
|
b->m_tip_flow = .5f;
|
|
b->m_tip_spacing = .1f;
|
|
b->m_tip_opacity = 1.f;
|
|
Canvas::I->m_current_brush = b;
|
|
|
|
// BRUSH PRESETS
|
|
|
|
m_preset_thumb = find<NodeImage>("preset-thumb");
|
|
m_preset_thumb->m_use_mipmaps = true;
|
|
m_preset_preview = find<NodeStrokePreview>("preset-preview");
|
|
m_preset_preview->m_brush = b;
|
|
m_preset_preview->draw_stroke();
|
|
m_preset_button = find<NodeButtonCustom>("preset-button");
|
|
m_preset_button->on_click = [this](Node*) {
|
|
auto screen = root()->m_size;
|
|
glm::vec2 pos = m_preset_button->m_pos + glm::vec2(m_preset_button->m_size.x, 0);
|
|
root()->add_child(m_presets_popup);
|
|
auto tick = root()->add_child<NodeImage>();
|
|
tick->SetPositioning(YGPositionTypeAbsolute);
|
|
tick->SetSize(16, 32);
|
|
tick->SetPosition(pos.x, pos.y + (m_preset_button->m_size.y - 32) * 0.5f);
|
|
tick->set_image("data/ui/popup-tick.png");
|
|
root()->update();
|
|
if ((pos.y + m_presets_popup->m_size.y) > screen.y)
|
|
pos.y = screen.y - m_presets_popup->m_size.y;
|
|
if (pos.y < 0)
|
|
pos.y = 0;
|
|
m_presets_popup->SetPosition(pos.x + 16, pos.y);
|
|
m_presets_popup->mouse_capture();
|
|
root()->update();
|
|
|
|
m_presets_popup->on_popup_close = [this, tick](Node*) {
|
|
tick->destroy();
|
|
};
|
|
|
|
m_presets_popup->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
|
|
// don't change some params
|
|
b->m_tip_size = Canvas::I->m_current_brush->m_tip_size;
|
|
b->m_tip_color = Canvas::I->m_current_brush->m_tip_color;
|
|
*Canvas::I->m_current_brush = *b;
|
|
m_preview->draw_stroke();
|
|
m_preset_preview->draw_stroke();
|
|
m_brush_thumb->set_image(b->m_brush_thumb_path);
|
|
m_dual_brush_thumb->set_image(b->m_dual_thumb_path);
|
|
m_preset_thumb->set_image(b->m_brush_thumb_path);
|
|
update_controls();
|
|
};
|
|
};
|
|
|
|
// BRUSH TIP SHAPE
|
|
|
|
m_brush_thumb = find<NodeImage>("tip-change-thumb");
|
|
m_brush_thumb->set_image(m_brush_popup->get_thumb_path(br_idx));
|
|
m_brush_button = find<NodeButtonCustom>("tip-change");
|
|
m_brush_button->on_click = [this](Node*) {
|
|
auto screen = root()->m_size;
|
|
glm::vec2 pos = m_brush_button->m_pos + glm::vec2(m_brush_button->m_size.x, 0);
|
|
root()->add_child(m_brush_popup);
|
|
auto tick = root()->add_child<NodeImage>();
|
|
tick->SetPositioning(YGPositionTypeAbsolute);
|
|
tick->SetSize(16, 32);
|
|
tick->SetPosition(pos.x, pos.y + (m_brush_button->m_size.y - 32) * 0.5f);
|
|
tick->set_image("data/ui/popup-tick.png");
|
|
root()->update();
|
|
if ((pos.y + m_brush_popup->m_size.y) > screen.y)
|
|
pos.y = screen.y - m_brush_popup->m_size.y;
|
|
if (pos.y < 0)
|
|
pos.y = 0;
|
|
m_brush_popup->SetPosition(pos.x + 16, pos.y);
|
|
m_brush_popup->mouse_capture();
|
|
root()->update();
|
|
|
|
m_brush_popup->on_popup_close = [this, tick](Node*) {
|
|
tick->destroy();
|
|
};
|
|
|
|
m_brush_popup->on_brush_changed = [this](Node*, int index) {
|
|
if (on_brush_changed)
|
|
on_brush_changed(this, m_brush_popup->get_texture_path(index), m_brush_popup->get_thumb_path(index));
|
|
m_brush_thumb->set_image(m_brush_popup->get_thumb_path(index));
|
|
//m_brush_popup->mouse_release();
|
|
//m_brush_popup->parent->remove_child(m_brush_popup.get());
|
|
};
|
|
};
|
|
|
|
// DUAL BRUSH TIP SHAPE
|
|
|
|
m_dual_brush_thumb = find<NodeImage>("dual-change-thumb");
|
|
m_dual_brush_thumb->set_image(m_brush_popup->get_thumb_path(br_idx));
|
|
m_dual_brush_button = find<NodeButtonCustom>("dual-change");
|
|
m_dual_brush_button->on_click = [this](Node*) {
|
|
auto screen = root()->m_size;
|
|
glm::vec2 pos = m_dual_brush_button->m_pos + glm::vec2(m_dual_brush_button->m_size.x, 0);
|
|
root()->add_child(m_brush_popup);
|
|
auto tick = root()->add_child<NodeImage>();
|
|
tick->SetPositioning(YGPositionTypeAbsolute);
|
|
tick->SetSize(16, 32);
|
|
tick->SetPosition(pos.x, pos.y + (m_dual_brush_button->m_size.y - 32) * 0.5f);
|
|
tick->set_image("data/ui/popup-tick.png");
|
|
root()->update();
|
|
if ((pos.y + m_brush_popup->m_size.y) > screen.y)
|
|
pos.y = screen.y - m_brush_popup->m_size.y;
|
|
if (pos.y < 0)
|
|
pos.y = 0;
|
|
m_brush_popup->SetPosition(pos.x + 16, pos.y);
|
|
m_brush_popup->mouse_capture();
|
|
root()->update();
|
|
|
|
m_brush_popup->on_popup_close = [this, tick](Node*) {
|
|
tick->destroy();
|
|
};
|
|
|
|
m_brush_popup->on_brush_changed = [this](Node*, int index) {
|
|
if (on_dual_changed)
|
|
on_dual_changed(this, m_brush_popup->get_texture_path(index), m_brush_popup->get_thumb_path(index));
|
|
m_dual_brush_thumb->set_image(m_brush_popup->get_thumb_path(index));
|
|
};
|
|
};
|
|
|
|
|
|
// PATTERN IMAGE
|
|
|
|
m_pattern_thumb = find<NodeImage>("pattern-change-thumb");
|
|
m_pattern_thumb->set_image(m_pattern_popup->get_thumb_path(0));
|
|
m_pattern_button = find<NodeButtonCustom>("pattern-change");
|
|
m_pattern_button->on_click = [this](Node*) {
|
|
auto screen = root()->m_size;
|
|
glm::vec2 pos = m_pattern_button->m_pos + glm::vec2(m_pattern_button->m_size.x, 0);
|
|
root()->add_child(m_pattern_popup);
|
|
auto tick = root()->add_child<NodeImage>();
|
|
tick->SetPositioning(YGPositionTypeAbsolute);
|
|
tick->SetSize(16, 32);
|
|
tick->SetPosition(pos.x, pos.y + (m_pattern_button->m_size.y - 32) * 0.5f);
|
|
tick->set_image("data/ui/popup-tick.png");
|
|
root()->update();
|
|
if ((pos.y + m_pattern_popup->m_size.y) > screen.y)
|
|
pos.y = screen.y - m_pattern_popup->m_size.y;
|
|
if (pos.y < 0)
|
|
pos.y = 0;
|
|
m_pattern_popup->SetPosition(pos.x + 16, pos.y);
|
|
m_pattern_popup->mouse_capture();
|
|
root()->update();
|
|
|
|
m_pattern_popup->on_popup_close = [this, tick](Node*) {
|
|
tick->destroy();
|
|
};
|
|
|
|
m_pattern_popup->on_brush_changed = [this](Node*, int index) {
|
|
if (on_pattern_changed)
|
|
on_pattern_changed(this, m_pattern_popup->get_texture_path(index), m_pattern_popup->get_thumb_path(index));
|
|
m_pattern_thumb->set_image(m_pattern_popup->get_thumb_path(index));
|
|
};
|
|
};
|
|
|
|
|
|
m_preview = find<NodeStrokePreview>("canvas");
|
|
m_blend_mode = find<NodeComboBox>("blend-mode");
|
|
m_blend_mode->on_select = [this](Node*, int index) {
|
|
Canvas::I->m_current_brush->m_blend_mode = index;
|
|
m_preview->draw_stroke();
|
|
if (on_stroke_change)
|
|
on_stroke_change(this);
|
|
};
|
|
|
|
init_slider(m_tip_size, "tip-size", &Brush::m_tip_size);
|
|
init_slider(m_tip_spacing, "tip-spacing", &Brush::m_tip_spacing);
|
|
init_slider(m_tip_flow, "tip-flow", &Brush::m_tip_flow);
|
|
init_slider(m_tip_opacity, "tip-opacity", &Brush::m_tip_opacity);
|
|
init_slider(m_tip_angle, "tip-angle", &Brush::m_tip_angle);
|
|
init_slider(m_tip_angle_delay, "tip-angle-delay", &Brush::m_tip_angle_delay);
|
|
init_slider(m_tip_mix, "tip-mix", &Brush::m_tip_mix);
|
|
init_slider(m_pattern_opacity, "pattern-opacity", &Brush::m_pattern_opacity);
|
|
init_slider(m_tip_wet, "tip-wet", &Brush::m_tip_wet);
|
|
init_slider(m_tip_noise, "tip-noise", &Brush::m_tip_noise);
|
|
init_slider(m_tip_hue, "tip-hue", &Brush::m_tip_hue);
|
|
init_slider(m_tip_sat, "tip-sat", &Brush::m_tip_sat);
|
|
init_slider(m_tip_val, "tip-val", &Brush::m_tip_val);
|
|
init_slider(m_jitter_scale, "jitter-scale", &Brush::m_jitter_scale);
|
|
init_slider(m_jitter_angle, "jitter-angle", &Brush::m_jitter_angle);
|
|
init_slider(m_jitter_spread, "jitter-spread", &Brush::m_jitter_spread);
|
|
init_slider(m_jitter_flow, "jitter-flow", &Brush::m_jitter_flow);
|
|
init_slider(m_jitter_hue, "jitter-hue", &Brush::m_jitter_hue);
|
|
init_slider(m_jitter_sat, "jitter-sat", &Brush::m_jitter_sat);
|
|
init_slider(m_jitter_val, "jitter-val", &Brush::m_jitter_val);
|
|
|
|
init_checkbox(m_tip_angle_follow, "tip-angle-follow", &Brush::m_tip_angle_follow);
|
|
init_checkbox(m_tip_flow_pressure, "tip-flow-pressure", &Brush::m_tip_flow_pressure);
|
|
init_checkbox(m_tip_size_pressure, "tip-size-pressure", &Brush::m_tip_size_pressure);
|
|
|
|
init_checkbox(m_tip_invert, "tip-invert", &Brush::m_tip_invert);
|
|
init_checkbox(m_tip_flipx, "tip-flipx", &Brush::m_tip_flipx);
|
|
init_checkbox(m_tip_flipy, "tip-flipy", &Brush::m_tip_flipy);
|
|
init_checkbox(m_pattern_enabled, "pattern-enabled", &Brush::m_pattern_enabled);
|
|
init_checkbox(m_dual_enabled, "dual-enabled", &Brush::m_dual_enabled);
|
|
init_checkbox(m_dual_scatter_axis, "dual-scatter-axis", &Brush::m_dual_scatter_axis);
|
|
init_checkbox(m_dual_invert, "dual-invert", &Brush::m_dual_invert);
|
|
init_checkbox(m_dual_flipx, "dual-flipx", &Brush::m_dual_flipx);
|
|
init_checkbox(m_dual_flipy, "dual-flipy", &Brush::m_dual_flipy);
|
|
init_checkbox(m_dual_randflip, "dual-randflip", &Brush::m_dual_randflip);
|
|
init_checkbox(m_tip_randflipx, "tip-randflipx", &Brush::m_tip_randflipx);
|
|
init_checkbox(m_tip_randflipy, "tip-randflipy", &Brush::m_tip_randflipy);
|
|
init_checkbox(m_pattern_eachsample, "pattern-eachsample", &Brush::m_pattern_eachsample);
|
|
|
|
init_checkbox(m_pattern_invert, "pattern-invert", &Brush::m_pattern_invert);
|
|
init_checkbox(m_pattern_flipx, "pattern-flipx", &Brush::m_pattern_flipx);
|
|
init_checkbox(m_pattern_flipy, "pattern-flipy", &Brush::m_pattern_flipy);
|
|
|
|
init_slider(m_dual_size, "dual-size", &Brush::m_dual_size);
|
|
init_slider(m_dual_spacing, "dual-spacing", &Brush::m_dual_spacing);
|
|
init_slider(m_dual_scatter, "dual-scatter", &Brush::m_dual_scatter);
|
|
init_slider(m_tip_aspect, "tip-aspect", &Brush::m_tip_aspect);
|
|
init_slider(m_dual_opacity, "dual-opacity", &Brush::m_dual_opacity);
|
|
init_slider(m_dual_flow, "dual-flow", &Brush::m_dual_flow);
|
|
init_slider(m_dual_rotate, "dual-rotate", &Brush::m_dual_rotate);
|
|
init_slider(m_pattern_scale, "pattern-scale", &Brush::m_pattern_scale);
|
|
init_slider(m_pattern_brightness, "pattern-brightness", &Brush::m_pattern_brightness);
|
|
init_slider(m_pattern_contrast, "pattern-contrast", &Brush::m_pattern_contrast);
|
|
|
|
auto curve_cubic = [](float v) { return glm::pow(v, 3.f); };
|
|
auto curve_quad = [](float v) { return glm::pow(v, 2.f); };
|
|
m_curves[m_tip_size] = curve_cubic;
|
|
m_curves[m_tip_spacing] = curve_quad;
|
|
m_curves[m_tip_flow] = curve_quad;
|
|
m_curves[m_dual_size] = curve_cubic;
|
|
m_curves[m_dual_spacing] = curve_quad;
|
|
m_curves[m_dual_flow] = curve_quad;
|
|
|
|
m_tip_aspect_reset = find<NodeButtonCustom>("tip-aspect-reset");
|
|
m_tip_aspect_reset->on_click = [this](Node*) {
|
|
m_tip_aspect->set_value(0.5);
|
|
Canvas::I->m_current_brush->m_tip_aspect = 0.5f;
|
|
m_preview->draw_stroke();
|
|
if (on_stroke_change)
|
|
on_stroke_change(this);
|
|
};
|
|
|
|
m_dual_blend_mode = find<NodeComboBox>("dual-blend-mode");
|
|
m_dual_blend_mode->on_select = [this](Node*, int index) {
|
|
Canvas::I->m_current_brush->m_dual_blend_mode = index;
|
|
m_preview->draw_stroke();
|
|
if (on_stroke_change)
|
|
on_stroke_change(this);
|
|
};
|
|
|
|
m_pattern_blend_mode = find<NodeComboBox>("pattern-blend-mode");
|
|
m_pattern_blend_mode->on_select = [this](Node*, int index) {
|
|
Canvas::I->m_current_brush->m_pattern_blend_mode = index;
|
|
m_preview->draw_stroke();
|
|
if (on_stroke_change)
|
|
on_stroke_change(this);
|
|
};
|
|
|
|
m_preview->m_brush = Canvas::I->m_current_brush;
|
|
m_preview->draw_stroke();
|
|
|
|
/*
|
|
auto load_stencil = find<NodeButton>("tip-stencil-load");
|
|
load_stencil->on_click = [this](Node*) {
|
|
App::I.pick_image([this](std::string path) {
|
|
App::I.async_start();
|
|
if (TextureManager::load(path.c_str()))
|
|
{
|
|
if (on_pattern_changed)
|
|
on_pattern_changed(this, path, "");
|
|
}
|
|
App::I.async_redraw();
|
|
App::I.async_end();
|
|
});
|
|
};
|
|
*/
|
|
|
|
update_controls();
|
|
}
|
|
|
|
void NodePanelStroke::init_slider(NodeSliderH*& target, const char* id, float Brush::* prop)
|
|
{
|
|
target = find<NodeSliderH>(id);
|
|
target->on_value_changed = std::bind(&NodePanelStroke::handle_slide,
|
|
this, prop, std::placeholders::_1, std::placeholders::_2);
|
|
//m_canvas->m_brush->*prop = target->m_value.x;
|
|
}
|
|
|
|
void NodePanelStroke::handle_slide(float Brush::* prop, Node* target, float value)
|
|
{
|
|
auto curve = m_curves.find((NodeSliderH*)target);
|
|
Canvas::I->m_current_brush.get()->*prop = curve != m_curves.end() ? curve->second(value) : value;
|
|
m_preview->draw_stroke();
|
|
if (on_stroke_change)
|
|
on_stroke_change(this);
|
|
}
|
|
|
|
void NodePanelStroke::init_checkbox(NodeCheckBox*& target, const char* id, bool Brush::* prop)
|
|
{
|
|
target = find<NodeCheckBox>(id);
|
|
target->on_value_changed = std::bind(&NodePanelStroke::handle_checkbox,
|
|
this, prop, std::placeholders::_1, std::placeholders::_2);
|
|
Canvas::I->m_current_brush.get()->*prop = target->checked;
|
|
}
|
|
|
|
void NodePanelStroke::handle_checkbox(bool Brush::* prop, Node *target, bool value)
|
|
{
|
|
Canvas::I->m_current_brush.get()->*prop = value;
|
|
m_preview->draw_stroke();
|
|
if (on_stroke_change)
|
|
on_stroke_change(this);
|
|
}
|