From 3cea02995a70e15394892192f84ae95693434b50 Mon Sep 17 00:00:00 2001 From: Vladimir Sadovnikov Date: Mon, 10 Jul 2023 21:43:26 +0300 Subject: [PATCH] Added meter feature to knob --- CHANGELOG | 1 + include/lsp-plug.in/tk/widgets/simple/Knob.h | 36 +++++++------ src/main/widgets/simple/Knob.cpp | 36 +++++++++++-- src/test/mtest/widgets/simple/knob.cpp | 55 +++++++++++++++++++- 4 files changed, 108 insertions(+), 20 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 37c175be..522e8126 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ * Optimized window move operation (does not require realize if window size did not change). * Implemented graph line segment widget. +* Added meter feature to knob. * Updated build scripts. === 1.0.11 === diff --git a/include/lsp-plug.in/tk/widgets/simple/Knob.h b/include/lsp-plug.in/tk/widgets/simple/Knob.h index 69d12266..d8e3cfb4 100644 --- a/include/lsp-plug.in/tk/widgets/simple/Knob.h +++ b/include/lsp-plug.in/tk/widgets/simple/Knob.h @@ -40,16 +40,20 @@ namespace lsp prop::Color sHoleColor; prop::Color sTipColor; prop::Color sBalanceTipColor; + prop::Color sMeterColor; prop::SizeRange sSizeRange; prop::Float sScale; prop::RangeFloat sValue; prop::StepFloat sStep; prop::Float sBalance; + prop::Float sMeterMin; + prop::Float sMeterMax; prop::Boolean sCycling; prop::Boolean sScaleMarks; prop::Boolean sBalanceColorCustom; prop::Boolean sFlat; prop::Boolean sScaleActive; + prop::Boolean sMeterActive; prop::Integer sHoleSize; prop::Integer sGapSize; prop::Float sScaleBrightness; @@ -87,17 +91,21 @@ namespace lsp prop::Color sHoleColor; prop::Color sTipColor; prop::Color sBalanceTipColor; + prop::Color sMeterColor; prop::SizeRange sSizeRange; prop::Float sScale; prop::RangeFloat sValue; prop::StepFloat sStep; prop::Float sBalance; + prop::Float sMeterMin; + prop::Float sMeterMax; prop::Boolean sCycling; prop::Boolean sScaleMarks; prop::Boolean sBalanceColorCustom; prop::Boolean sFlat; prop::Boolean sScaleActive; + prop::Boolean sMeterActive; prop::Integer sHoleSize; prop::Integer sGapSize; prop::Float sScaleBrightness; @@ -116,14 +124,14 @@ namespace lsp static status_t slot_end_edit(Widget *sender, void *ptr, void *data); protected: - virtual void size_request(ws::size_limit_t *r); - virtual void property_changed(Property *prop); + virtual void size_request(ws::size_limit_t *r) override; + virtual void property_changed(Property *prop) override; public: explicit Knob(Display *dpy); - virtual ~Knob(); + virtual ~Knob() override; - virtual status_t init(); + virtual status_t init() override; public: LSP_TK_PROPERTY(Color, color, &sColor) @@ -132,16 +140,20 @@ namespace lsp LSP_TK_PROPERTY(Color, hole_color, &sHoleColor) LSP_TK_PROPERTY(Color, tip_color, &sTipColor) LSP_TK_PROPERTY(Color, balance_tip_color, &sBalanceTipColor) + LSP_TK_PROPERTY(Color, meter_color, &sMeterColor) LSP_TK_PROPERTY(SizeRange, size, &sSizeRange) LSP_TK_PROPERTY(Float, scale, &sScale) LSP_TK_PROPERTY(RangeFloat, value, &sValue) LSP_TK_PROPERTY(StepFloat, step, &sStep) LSP_TK_PROPERTY(Float, balance, &sBalance) + LSP_TK_PROPERTY(Float, meter_min, &sMeterMin) + LSP_TK_PROPERTY(Float, meter_max, &sMeterMax) LSP_TK_PROPERTY(Boolean, cycling, &sCycling) LSP_TK_PROPERTY(Boolean, scale_marks, &sScaleMarks) LSP_TK_PROPERTY(Boolean, balance_color_custom, &sBalanceColorCustom) LSP_TK_PROPERTY(Boolean, flat, &sFlat) LSP_TK_PROPERTY(Boolean, scale_active, &sScaleActive) + LSP_TK_PROPERTY(Boolean, meter_active, &sMeterActive) LSP_TK_PROPERTY(Integer, hole_size, &sHoleSize) LSP_TK_PROPERTY(Integer, gap_size, &sGapSize) LSP_TK_PROPERTY(Float, scale_brightness, &sScaleBrightness) @@ -150,21 +162,15 @@ namespace lsp LSP_TK_PROPERTY(Boolean, invert_mouse_vscroll, &sInvertMouseVScroll) public: - virtual status_t on_mouse_down(const ws::event_t *e); - - virtual status_t on_mouse_up(const ws::event_t *e); - - virtual status_t on_mouse_move(const ws::event_t *e); - - virtual status_t on_mouse_scroll(const ws::event_t *e); + virtual status_t on_mouse_down(const ws::event_t *e) override; + virtual status_t on_mouse_up(const ws::event_t *e) override; + virtual status_t on_mouse_move(const ws::event_t *e) override; + virtual status_t on_mouse_scroll(const ws::event_t *e) override; + virtual void draw(ws::ISurface *s) override; virtual status_t on_begin_edit(); - virtual status_t on_change(); - virtual status_t on_end_edit(); - - virtual void draw(ws::ISurface *s); }; } /* namespace tk */ diff --git a/src/main/widgets/simple/Knob.cpp b/src/main/widgets/simple/Knob.cpp index dd02b0fc..2f051c02 100644 --- a/src/main/widgets/simple/Knob.cpp +++ b/src/main/widgets/simple/Knob.cpp @@ -38,16 +38,20 @@ namespace lsp sHoleColor.bind("hole.color", this); sTipColor.bind("tip.color", this); sBalanceTipColor.bind("balance.tip.color", this); + sMeterColor.bind("meter.color", this); sSizeRange.bind("size.range", this); sScale.bind("scale.size", this); sValue.bind("value", this); sStep.bind("step", this); sBalance.bind("value.balance", this); + sMeterMin.bind("meter.min", this); + sMeterMax.bind("meter.max", this); sCycling.bind("value.cycling", this); sScaleMarks.bind("scale.marks", this); sBalanceColorCustom.bind("balance.color.custom", this); sFlat.bind("flat", this); sScaleActive.bind("scale.active", this); + sMeterActive.bind("meter.active", this); sHoleSize.bind("hole.size", this); sGapSize.bind("gap.size", this); sScaleBrightness.bind("scale.brightness", this); @@ -59,6 +63,7 @@ namespace lsp sScaleColor.set("#00cc00"); sBalanceColor.set("#0000cc"); sHoleColor.set("#000000"); + sMeterColor.set("#88ff0000"); sTipColor.set("#000000"); sBalanceTipColor.set("#0000ff"); sSizeRange.set(8, -1); @@ -66,11 +71,14 @@ namespace lsp sValue.set_all(0.5f, 0.0f, 1.0f); sStep.set(0.01f); sBalance.set(0.5f); + sMeterMin.set(0.0f); + sMeterMax.set(0.0f); sCycling.set(false); sScaleMarks.set(true); sBalanceColorCustom.set(false); sFlat.set(false); sScaleActive.set(true); + sMeterActive.set(false); sHoleSize.set(1); sGapSize.set(1); sScaleBrightness.set(0.75f); @@ -91,16 +99,20 @@ namespace lsp sHoleColor(&sProperties), sTipColor(&sProperties), sBalanceTipColor(&sProperties), + sMeterColor(&sProperties), sSizeRange(&sProperties), sScale(&sProperties), sValue(&sProperties), sStep(&sProperties), sBalance(&sProperties), + sMeterMin(&sProperties), + sMeterMax(&sProperties), sCycling(&sProperties), sScaleMarks(&sProperties), sBalanceColorCustom(&sProperties), sFlat(&sProperties), sScaleActive(&sProperties), + sMeterActive(&sProperties), sHoleSize(&sProperties), sGapSize(&sProperties), sScaleBrightness(&sProperties), @@ -131,16 +143,20 @@ namespace lsp sHoleColor.bind("hole.color", &sStyle); sTipColor.bind("tip.color", &sStyle); sBalanceTipColor.bind("balance.tip.color", &sStyle); + sMeterColor.bind("meter.color", &sStyle); sSizeRange.bind("size.range", &sStyle); sScale.bind("scale.size", &sStyle); sValue.bind("value", &sStyle); sStep.bind("step", &sStyle); sBalance.bind("value.balance", &sStyle); + sMeterMin.bind("meter.min", &sStyle); + sMeterMax.bind("meter.max", &sStyle); sCycling.bind("value.cycling", &sStyle); sScaleMarks.bind("scale.marks", &sStyle); sBalanceColorCustom.bind("balance.color.custom", &sStyle); sFlat.bind("flat", &sStyle); sScaleActive.bind("scale.active", &sStyle); + sMeterActive.bind("meter.active", &sStyle); sHoleSize.bind("hole.size", &sStyle); sGapSize.bind("gap.size", &sStyle); sScaleBrightness.bind("scale.brightness", &sStyle); @@ -161,15 +177,18 @@ namespace lsp { Widget::property_changed(prop); - if (prop->one_of(sColor, sScaleColor, sBalanceColor, sHoleColor, sTipColor, sBalanceTipColor)) + if (prop->one_of(sColor, sScaleColor, sBalanceColor, sHoleColor, sTipColor, sBalanceTipColor, sMeterColor)) query_draw(); if (prop->one_of(sSizeRange, sScale, sHoleSize, sGapSize)) query_resize(); - if (prop->one_of(sValue, sBalance, sCycling, sScaleMarks, sBalanceColorCustom, sFlat, sScaleBrightness, + if (prop->one_of(sValue, sBalance, sMeterMin, sMeterMax, sCycling, sScaleMarks, sBalanceColorCustom, sFlat, sScaleBrightness, sBalanceTipSize, sBalanceTipColorCustom)) query_draw(); + + if (prop->one_of(sScaleActive, sMeterActive)) + query_draw(); } status_t Knob::slot_on_change(Widget *sender, void *ptr, void *data) @@ -398,6 +417,8 @@ namespace lsp float bright = sBrightness.get(); float value = sValue.get_normalized(); float balance = sValue.get_normalized(sBalance.get()); + float meter_min = sValue.get_normalized(sMeterMin.get()); + float meter_max = sValue.get_normalized(sMeterMax.get()); // Calculate knob parameters ssize_t c_x = (sSize.nWidth >> 1); @@ -409,7 +430,7 @@ namespace lsp size_t scale = lsp_max(0, sScale.get() * scaling); // Prepare the color palette - lsp::Color scol, sdcol; + lsp::Color scol, sdcol, mcol; if (sBalanceColorCustom.get()) { scol.copy(sBalanceColor); @@ -421,6 +442,7 @@ namespace lsp sdcol.copy(sScaleColor); sdcol.scale_hsl_lightness(sScaleBrightness.get()); } + mcol.copy(sMeterColor); lsp::Color hcol(sHoleColor); lsp::Color bg_color; @@ -435,7 +457,7 @@ namespace lsp bool aa = s->set_antialiasing(true); size_t nsectors; - float delta, base, v_angle1, v_angle2; + float delta, base, v_angle1, v_angle2, m_angle1, m_angle2; if (sCycling.get()) { nsectors = 24; @@ -443,6 +465,8 @@ namespace lsp base = 1.5f * M_PI + balance * delta; v_angle2 = base; v_angle1 = base + value * delta; + m_angle1 = base + meter_min * delta; + m_angle2 = base + meter_max * delta; } else { @@ -451,6 +475,8 @@ namespace lsp base = 2.0f * M_PI / 3.0f; v_angle1 = base + value * delta; v_angle2 = base + balance * delta; + m_angle1 = base + meter_min * delta; + m_angle2 = base + meter_max * delta; } // Draw scale @@ -469,6 +495,8 @@ namespace lsp else s->fill_sector(scol, c_x, c_y, xr, v_angle2, v_angle1); } + if (sMeterActive.get()) + s->fill_sector(mcol, c_x, c_y, xr, m_angle1, m_angle2); if (sScaleMarks.get()) { diff --git a/src/test/mtest/widgets/simple/knob.cpp b/src/test/mtest/widgets/simple/knob.cpp index ded055b9..03a12fd8 100644 --- a/src/test/mtest/widgets/simple/knob.cpp +++ b/src/test/mtest/widgets/simple/knob.cpp @@ -177,6 +177,18 @@ MTEST_BEGIN("tk.widgets.simple", knob) } } + static status_t timer_handler(ws::timestamp_t sched, ws::timestamp_t time, void *arg) + { + lltl::parray *timer_knobs = static_cast *>(arg); + + for (lltl::iterator it = timer_knobs->values(); it; ++it) + { + it->meter_max()->set(0.5f + 0.5f * sinf((time % 1000) * M_PI * 0.002)); + } + + return STATUS_OK; + } + MTEST_MAIN { lltl::parray vh; @@ -187,11 +199,16 @@ MTEST_BEGIN("tk.widgets.simple", knob) MTEST_ASSERT(dpy->init(0, NULL) == STATUS_OK); lltl::parray widgets; + lltl::parray timer_knobs; tk::Widget *w = NULL; tk::Window *wnd = new tk::Window(dpy); tk::Grid *grid = NULL; tk::Knob *kn = NULL; + tk::Timer tmr; + tmr.set_handler(timer_handler, &timer_knobs); + tmr.bind(dpy); + // Initialize window MTEST_ASSERT(init_widget(wnd, vh, "window") == STATUS_OK); MTEST_ASSERT(wnd->title()->set_raw("Test knob") == STATUS_OK); @@ -215,7 +232,7 @@ MTEST_BEGIN("tk.widgets.simple", knob) MTEST_ASSERT(wnd->add(grid) == STATUS_OK); grid->bg_color()->set_rgb(1.0f, 1.0f, 1.0f); grid->padding()->set(8); - grid->rows()->set(5); + grid->rows()->set(6); grid->columns()->set(4); grid->orientation()->set_horizontal(); grid->hspacing()->set(2); @@ -311,12 +328,48 @@ MTEST_BEGIN("tk.widgets.simple", knob) kn->hole_size()->set(0); kn->gap_size()->set(0); } + + // Create knob + for (size_t x=0; x<4; ++x) + { + MTEST_ASSERT(id.fmt_ascii("knob-%d-5", x)); + MTEST_ASSERT(kn = new tk::Knob(dpy)); + MTEST_ASSERT(init_widget(kn, vh, id.get_ascii()) == STATUS_OK); + MTEST_ASSERT(widgets.push(kn)); + MTEST_ASSERT(grid->add(kn) == STATUS_OK); + + kn->balance()->set(x * 0.25f); + kn->size()->set((x+3) * 8); + kn->scale_color()->set_rgb24(0x2d7990); + kn->balance_color()->set_rgb24(0x91d870); + kn->bg_color()->set_rgb24(0x24272e); + kn->cycling()->set(x & 1); + kn->scale_marks()->set(false); + kn->scale()->set(4.0f); + kn->balance_color_custom()->set(true); + kn->color()->set_rgb24(0x24272e); + kn->tip_color()->set_rgb24(0xa8aed3); + kn->flat()->set(true); + kn->hole_size()->set(0); + kn->gap_size()->set(0); + + kn->meter_active()->set(true); + kn->meter_max()->set(0.8); + kn->meter_min()->set(x * 0.25); + kn->meter_color()->alpha(0.75); + + MTEST_ASSERT(timer_knobs.add(kn)); + } + + // Launch animation + tmr.launch(0, 40); } // Show window wnd->visibility()->set(true); MTEST_ASSERT(dpy->main() == STATUS_OK); + tmr.cancel(); while ((w = widgets.pop()) != NULL) {