Go Back   Cockos Incorporated Forums > REAPER Forums > ReaScript, JSFX, REAPER Plug-in Extensions, Developer Forum

Reply
 
Thread Tools Display Modes
Old 05-12-2022, 07:20 AM   #161
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
REAPER's own my_getViewport() which is what rtk uses).
Of course! :facepalm:

Quote:
Just not implemented yet, is all. I'll look into it and see if it's easy.
No rush, I can do a little math!
MonkeyBars is offline   Reply With Quote
Old 05-12-2022, 06:44 PM   #162
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

I have hit a snag that seems like a bug. I am attempting to define the icon attr on a Button, but rtk can't find it "in any icon path". The image is in the same folder as the script called by the action. I even sent rtk.script_path to the console and it's definitely correct. I also tried concatenating rtk.script_path before the image filename – same result, but with the correct macOS absolute path correctly dumped to the console by rtk. What gives?
MonkeyBars is offline   Reply With Quote
Old 05-12-2022, 07:10 PM   #163
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Nitpick: the circular Button calculates size by radius rather than diameter. I find that unintuitive

Another: the dropdown arrow in the OptionMenu is not editable, so one cannot create very small dropdowns. Ideally they would default to vertically centered, or at least respond to valign. It does respond to padding, but it doesn't respond the same way as text does, so that's not very convenient...

Last edited by MonkeyBars; 05-12-2022 at 07:24 PM.
MonkeyBars is offline   Reply With Quote
Old 05-12-2022, 07:12 PM   #164
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
I have hit a snag that seems like a bug. I am attempting to define the icon attr on a Button, but rtk can't find it "in any icon path".
This error may be misleading. It could be that the file exists, but REAPER just fails to load it. (I have uncommitted code to distinguish between these two conditions and improve the error message, but it needs more baking.)

I have run into this in the past where REAPER barfs on legitimate images simply because they have the wrong extension (like a jpg file named as png or vice versa). But I've just tested loading an image in the same path as the script and it's working here with the current dev build.

You can try bypassing rtk here and just use gfx.loadimg() directly. Does it return -1 or the image id?
tack is offline   Reply With Quote
Old 05-12-2022, 07:25 PM   #165
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
REAPER barfs on legitimate images simply because they have the wrong extension
Legit png file here

Quote:
You can try bypassing rtk here and just use gfx.loadimg() directly. Does it return -1 or the image id?
Image ID, all good here with gfx.loadimg(). Pasting the exact same string into the Button attrs isn't found by rtk.
MonkeyBars is offline   Reply With Quote
Old 05-12-2022, 07:26 PM   #166
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
Nitpick: the circular Button calculates size by radius rather than diameter. I find that unintuitive
Hm this shouldn't be the case. The width of the button should dictate the diameter. I'm staring at the code here that calculates the radius of circle as w/2. Will look more into this tomorrow.

Quote:
Originally Posted by MonkeyBars View Post
Another: the dropdown arrow in the OptionMenu is not editable, so one cannot create very small dropdowns.
It's not really a goal of rtk to be skinnable. So don't expect a lot option here. That said, rtk.OptionMenu subclasses rtk.Button and the icon attribute is used for the dropdown arrow, which is even documented in the OptionMenu docs. So this should be changeable. Just tested this and it's working here.

Quote:
Originally Posted by MonkeyBars View Post
Ideally they would default to vertically centered, or at least respond to valign.
The icon is vertically centered by default, and the icon does respond to valign (as usual for buttons).

I feel like we may be looking at different things. I think I'll need to see a test case and screenshot of what you're observing to see if there's a bug here that needs to be fixed.
tack is offline   Reply With Quote
Old 05-12-2022, 07:28 PM   #167
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
Image ID, all good here with gfx.loadimg(). Pasting the exact same string into the Button attrs isn't found by rtk.
Can you show me the code?
tack is offline   Reply With Quote
Old 05-12-2022, 07:29 PM   #168
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
It's not really a goal of rtk to be skinnable. So don't expect a lot option here.
Sorry, I just meant it doesn't center at small heights so it would be great to have independent control over that somehow... but ideally it would always just be centered I think.

Quote:
The icon is vertically centered by default, and the icon does respond to valign (as usual for buttons).

I feel like we may be looking at different things. I think I'll need to see a test case and screenshot of what you're observing to see if there's a bug here that needs to be fixed.
Sorry my messages were ambiguous. Please take a look at OptionMenu with short height. E.g. I'm using h = 20 and fontscale = 0.65 and both the arrow and text are several pixels below center. This wouldn't be that big a deal, but they each respond differently to negative top padding values. It seems the arrow moves more? Kind of confused by what I'm seeing.

Last edited by MonkeyBars; 05-12-2022 at 07:38 PM.
MonkeyBars is offline   Reply With Quote
Old 05-12-2022, 07:31 PM   #169
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
Can you show me the code?
Code:
rtk.Button{icon = "table_mute_off.png", w = 23, h = 20, lmargin = 5, tooltip = "Mute"}
I have tried with no other attrs and with various combinations of paths pointing to the file that gfx finds with no issues.
MonkeyBars is offline   Reply With Quote
Old 05-12-2022, 07:45 PM   #170
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
Code:
rtk.Button{icon = "table_mute_off.png", w = 23, h = 20, lmargin = 5, tooltip = "Mute"}
The irony here is my use icons in rtk with Reaticulate is fairly sophisticated, using rtk.ImagePack with multiple DPIs and light/dark variants, I failed to notice the most basic case above is broken.

Well, the icon value isn't supposed to contain the .png extension, but even so it doesn't work out of the box.

Until I fix things, you can unblock yourself for now by adding this line:

Code:
rtk.add_image_search_path(rtk.script_path, rtk.theme.name)
tack is offline   Reply With Quote
Old 05-12-2022, 07:54 PM   #171
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
E.g. I'm using h = 20 and fontscale = 0.65
I'd avoid specifying h this way because if the button actually needs more space to draw it's going to clip, which would certainly cause the misalignment you're seeing.

fontscale will take care of shrinking the font, and if you want a smaller arrow, you can do something like this:

Code:
optionmenu:attr('icon', optionmenu.icon:scale(14, 14))
You can also take down the padding if you want to further shrink it.

As a general rule, assigning w/h should be done sparingly as these are blunt hammers and fight against the automatic sizing that rtk does.
tack is offline   Reply With Quote
Old 05-12-2022, 09:29 PM   #172
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
You can also take down the padding if you want to further shrink it.

As a general rule, assigning w/h should be done sparingly as these are blunt hammers and fight against the automatic sizing that rtk does.
Buttons appear to size themselves off the font, which scales differently in different OSes, so to get a consistent result, it's helpful to be able to define a strict height for design purposes.
MonkeyBars is offline   Reply With Quote
Old 05-12-2022, 11:19 PM   #173
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
Until I fix things, you can unblock yourself for now by adding this line:

Code:
rtk.add_image_search_path(rtk.script_path, rtk.theme.name)
Added near start of project. This removes the error and now displays a white rectangle at the correct size and location where the icon should go.

The next two icons I tried work right, so perhaps this is an off-by-one or path initialization thing

Last edited by MonkeyBars; 05-12-2022 at 11:27 PM.
MonkeyBars is offline   Reply With Quote
Old 05-12-2022, 11:30 PM   #174
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

I have a feature request for the Slider! Please add a value curve mechanism. That can be a simple "thumbx" between 0 - 1 as visual starting point around which the values stretch, or actual curve shapes from Reaper... think volume fader
MonkeyBars is offline   Reply With Quote
Old 05-13-2022, 06:09 AM   #175
dri_ft
Human being with feelings
 
Join Date: Aug 2019
Posts: 22
Default

I have been working with this for a couple of days and it's a really nice library, well done. I really like having an actual layout engine. Everything has been pretty easy to pick up; the docs are mostly very good and most of it is pretty intuitive in relation my experience as a web dev.

One widget it could use, I think, would be knobs - they take up much less space than sliders, especially when you need a grid of them. In the meantime, I'm trying to put together a NumberEntry subclass of Entry, which will store a numeric value and respond appropriately to mousewheel scrolling and dragging up and down. I have made some progress in writing a subclass (not really documented, as far as I can see, but I was able to work from the examples I could find in the code), but I'm having trouble getting the behaviour around its value right.

Entry has a string as its value attribute, and I'm not quite sure how to marry this up with the numeric value that NumberEntry would use. I tried two approaches: one, overriding the value attr and making it a numeric variable; two, having both a value attr and a numvalue attr and having the former depend on the latter. Neither seemed to be without its problems. Can you give any guidance on what approach you would suggest, tack? Thanks.

It would also be nice to right-align the text, but it doesn't seem that alignment can be set for Entry. Is that so? No big deal.
dri_ft is offline   Reply With Quote
Old 05-13-2022, 09:26 AM   #176
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

I second that knobs and draggable numeric text inputs should be at the top of the new Widgets list
MonkeyBars is offline   Reply With Quote
Old 05-13-2022, 09:54 AM   #177
dri_ft
Human being with feelings
 
Join Date: Aug 2019
Posts: 22
Default

If I get mine working right, I'll happily share them.
dri_ft is offline   Reply With Quote
Old 05-13-2022, 11:44 AM   #178
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by MonkeyBars View Post
a white rectangle at the correct size and location where the icon should go.
This is so strange. I tried reediting the png in Photoshop, saving as PNG-8 and PNG-24, but it still displays all white. The other 3 (also Button icon) images in the form aren't exhibiting this behavior and work as expected.

Edit: Okay I just inserted the on states and 2 of them have the same issue – the on state in this case is trying to grab pngs in the same directory and they're displaying as white rectangles.

Last edited by MonkeyBars; 05-13-2022 at 12:37 PM.
MonkeyBars is offline   Reply With Quote
Old 05-13-2022, 03:08 PM   #179
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by dri_ft View Post
I have been working with this for a couple of days and it's a really nice library, well done. I really like having an actual layout engine. Everything has been pretty easy to pick up; the docs are mostly very good and most of it is pretty intuitive in relation my experience as a web dev.
Great to hear that, thanks for the feedback dri_ft.


Quote:
Originally Posted by dri_ft View Post
One widget it could use, I think, would be knobs - they take up much less space than sliders, especially when you need a grid of them.
Yeah, no shortage of widgets still needed, that's for sure.

Knob is high up there, along with tabs. But higher priority for me -- because I need it for Reaticulate -- is a combobox with the ability to contain arbitrary, searchable elements. The native menu used by rtk.OptionMenu is just too unwieldy when you have a large number of nested submenus.

But after that, yeah, I think a knob widget is a good next one to knock down. There's nothing from material design I can lift from, but plenty of prior art in my collection of FX plugins.


Quote:
Originally Posted by dri_ft View Post
I have made some progress in writing a subclass (not really documented, as far as I can see, but I was able to work from the examples I could find in the code)
The subclass API is documented (also related), but it's true that there currently aren't any examples to draw from outside rtk's own source code.

Quote:
Originally Posted by dri_ft View Post
Entry has a string as its value attribute, and I'm not quite sure how to marry this up with the numeric value that NumberEntry would use. I tried two approaches: one, overriding the value attr and making it a numeric variable; two, having both a value attr and a numvalue attr and having the former depend on the latter. Neither seemed to be without its problems. Can you give any guidance on what approach you would suggest, tack? Thanks.
For rtk.Entry, the value attribute definitely does need to be a string. But apart from that, there are a lot of options, depending on how you want it to work. My first thought would be to override the calculate function for the value attribute so that it strips out any non-digit character. This way it would cover all interactions with the entry, keyboard input, pasting, programmatically setting it via attr(), etc. Something like this:

Code:
local NumericEntry = rtk.class('NumericEntry', rtk.Entry)
NumericEntry.register{
    value = rtk.Attribute{
        calculate=function(self, attr, value, target)
            return value:gsub('%D','')
        end
    }
}
When accessing the value attribute, you'd need to pass it through tonumber(). In the above example, empty values are allowed so tonumber(entry.value) can be nil. So, (tonumber(entry.value) or 0)*42, say. If empty values can't be allowed, you can just have calculate return '0' (or whatever default) instead of the empty string, so then you can be sure tonumber(entry.value) will always return an actual number.

Quote:
Originally Posted by dri_ft View Post
It would also be nice to right-align the text, but it doesn't seem that alignment can be set for Entry. Is that so? No big deal.
Yeah, halign isn't implemented on rtk.Entry yet. I think it should be straightforward enough -- will try to take a look this weekend.

Last edited by tack; 05-13-2022 at 03:24 PM.
tack is offline   Reply With Quote
Old 05-13-2022, 03:10 PM   #180
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
Buttons appear to size themselves off the font, which scales differently in different OSes, so to get a consistent result, it's helpful to be able to define a strict height for design purposes.
The intrinsic size of buttons is based on the padding, font size and amount of text (if applicable), and icon size (if applicable). So any of these can be tweaked to affect the button's size.

I think I'll take my cue from HTML here, where the overriding element height isn't actually the right way to scale the control.

Quote:
Originally Posted by MonkeyBars View Post
I have a feature request for the Slider! Please add a value curve mechanism. That can be a simple "thumbx" between 0 - 1 as visual starting point around which the values stretch, or actual curve shapes from Reaper... think volume fader
If you want non linearity, couldn't you apply the appropriate transformation function to the value coming out of the slider? Otherwise, can you expand a bit more on how you would want this to work with rtk.Slider?

Quote:
Originally Posted by MonkeyBars View Post
Edit: Okay I just inserted the on states and 2 of them have the same issue – the on state in this case is trying to grab pngs in the same directory and they're displaying as white rectangles.
This one sounds weird. May be related to the automatic recoloring of icons based on the luminance of the button surface.

Can you share the png file in question and the snippet of code where you are referencing the image? I'll take a closer look.

Quote:
Originally Posted by MonkeyBars View Post
I second that knobs and draggable numeric text inputs should be at the top of the new Widgets list
Is there any prior art on the draggable numeric text input I can steal from, or at least borrow inspiration from? Any existing plugins, or stuff within REAPER?

I know I've seen inputs with up/down arrows to change numeric values. (Though that seems to have fallen out of fashion in modern interface design.) But I don't think I've seen anything draggable. Would like to get a better sense of how others have implemented it to make sure that if I do implement anything here that it's idiomatic.
tack is offline   Reply With Quote
Old 05-13-2022, 03:26 PM   #181
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
If you want non linearity, couldn't you apply the appropriate transformation function to the value coming out of the slider? Otherwise, can you expand a bit more on how you would want this to work with rtk.Slider?
For example, on a volume slider I want to go between -inf and +12dB, the internal Reaper values are 0 to 4, with 1.0 as 0.0dB. The thumb goes as expected to 25%, but I would like it to sit at 67%.

I'm not exactly clear on how I could transform the value to make it display one way but get saved another. Could you give an example of how I could get the desired volume thumb placement described above?

Quote:
This one sounds weird. May be related to the automatic recoloring of icons based on the luminance of the button surface.
Running all these with surface = false.

Quote:
Can you share the png file in question and the snippet of code where you are referencing the image? I'll take a closer look.
They're just single-state crops of v6 buttons, mute ones resized a little:


The other four images are displaying correctly; the phase button is the only one that displays both states right.

Code:
routing_settings_objs = {
["mute"] = rtk.Button{icon = "table_mute_off", value = false, w = 23, h = 20, lmargin = 5, padding = 0, surface = false, tooltip = "Mute"},
["phase"] = rtk.Button{icon = "gen_phase_off", value = false, w = 10, h = 10, tooltip = "Reverse phase", margin = "4 0 0 5", padding = 0, circular = true},
["mono_stereo"] = rtk.Button{icon = "gen_mono_off", value = false, w = 23, h = 20, tooltip = "Mono/Stereo", lmargin = 5, padding = 0, surface = false},
["volume"] = rtk.Slider{"0.0", tooltip = "Volume", min = 0, max = 4, value = 1, tmargin = 8, color = orange},
["pan"] = rtk.Slider{tooltip = "Pan", min = -1, max = 1, tmargin = 8},
["midi_vel"] = rtk.Button{icon = "gen_midi_off", value = false, valign = "center", surface = false, tooltip = "Toggle Midi Velocity Volume"}
}

  routing_settings_objs.mute.onclick = function(self)
    toggleBtnState(self, "table_mute")
  end

  routing_settings_objs.phase.onclick = function(self)
    toggleBtnState(self, "gen_phase")
  end

  routing_settings_objs.mono_stereo.onclick = function(self)
    toggleBtnState(self, "gen_mono")
  end

  routing_settings_objs.midi_vel.onclick = function(self)
    toggleBtnState(self, "gen_midi")
  end

function toggleBtnState(btn, img_filename_base)
  if btn.value == false then
    btn:attr("value", true)
    btn:attr("icon", img_filename_base .. "_on")

  elseif btn.value == true then
    btn:attr("value", false)
    btn:attr("icon", img_filename_base .. "_off")
  end
end
Quote:
Is there any prior art on the draggable numeric text input I can steal from, or at least borrow inspiration from? Any existing plugins, or stuff within REAPER?
That's a good point – unfortunately imo, they're not used in Reaper much. Logic has a lot of them and I find them extremely useful since they combine form control with data feedback.

Last edited by MonkeyBars; 05-13-2022 at 03:33 PM.
MonkeyBars is offline   Reply With Quote
Old 05-13-2022, 05:32 PM   #182
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
Running all these with surface = false.
Reproduced here, thanks for providing the images and code. It turns out it actually was the icon auto-recoloring, because I made a mistake in the rtk.add_image_search_path() workaround I mentioned earlier.

Do this for now:

Code:
rtk.add_image_search_path(rtk.script_path, rtk.theme.iconstyle)
It's a total kludge of course. I'm going to focus on fixing this properly tomorrow (where I have a bunch of TODOs in related code that I will finally take care of it at the same time), and will respond to your other questions then as well. My hacking time is minimal tonight.

Last edited by tack; 05-13-2022 at 05:42 PM.
tack is offline   Reply With Quote
Old 05-13-2022, 05:59 PM   #183
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
I made a mistake in the rtk.add_image_search_path() workaround I mentioned earlier.

Do this for now:

Code:
rtk.add_image_search_path(rtk.script_path, rtk.theme.iconstyle)
Watch out for those bandaid bugs! WORKING now thank you!!!

Quote:
It's a total kludge of course. I'm going to focus on fixing this properly tomorrow (where I have a bunch of TODOs in related code that I will finally take care of it at the same time), and will respond to your other questions then as well. My hacking time is minimal tonight.
Fantastic! Loving rtk from top to bottom. One of these days I'll make a widget...
MonkeyBars is offline   Reply With Quote
Old 05-14-2022, 02:44 AM   #184
dri_ft
Human being with feelings
 
Join Date: Aug 2019
Posts: 22
Default

Quote:
Originally Posted by tack View Post
The subclass API is documented (also related), but it's true that there currently aren't any examples to draw from outside rtk's own source code.
I had missed that, thanks.

Quote:
Originally Posted by tack View Post
For rtk.Entry, the value attribute definitely does need to be a string. But apart from that, there are a lot of options, depending on how you want it to work. My first thought would be....
Interesting. Keeping value as a string and calculating a number from it makes sense. But if I wanted to keep a copy of that number around to avoid recalculating it repeatedly, is there a way of doing so that makes sense? Would it make sense to set it as an attribute? (and then update it with attr(), or sync()?) Or just stash it in the table as a plain old lua field?

In any case, I'll work on getting something working in just the way you suggest for now. Thanks for your suggestions.

Quote:
Originally Posted by tack View Post
Yeah, halign isn't implemented on rtk.Entry yet. I think it should be straightforward enough -- will try to take a look this weekend.
Cool, let me know if you get anywhere.
dri_ft is offline   Reply With Quote
Old 05-14-2022, 08:25 AM   #185
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by dri_ft View Post
But if I wanted to keep a copy of that number around to avoid recalculating it repeatedly, is there a way of doing so that makes sense?
In case your motivation is performance related, I'd want to talk you out of that. tonumber() is pretty cheap to call.

But assuming your intentions are pure ...


Quote:
Originally Posted by dri_ft View Post
Would it make sense to set it as an attribute? (and then update it with attr(), or sync()?) Or just stash it in the table as a plain old lua field?
You're on the right track, these are the right questions to ask.

The simplest and cheapest approach is your last idea, to store the numeric form -- let's call it 'numvalue' for sake of discussion -- as a new field in the widget, not technically a widget attribute. self.numvalue = tonumber(value) kind of thing. Then the numvalue field is just a read-only numeric proxy for the actual text entry value string.

Or, if you want numvalue to act more like a full-fledged attribute, where you can access it via the widget's calc() method, and eventually be compatible with reactive values which will come at some point (see first page on this thread for discussion), then sync() is technically the right approach, but it's an internal API right now. (I added it for an early draft of reactives but it's subject to change.) In this particular case, attr() would work fine, it doesn't need sync() (because numvalue doesn't have a calculate function you need to bypass, which is what sync() does), so no need to resort to a currently-internal API, attr() will do nicely.

Then the next question is where to actually implement either of the above options. There are two approaches we could take here: one is to add a setter function to the value attribute, and the other is to implement _handle_attr() in the subclass and intercept setting the 'value' attribute. Six of one, half a dozen of the other, really. From a readability perspective, I'd be inclined to go with the setter option:

Code:
local NumericEntry = rtk.class('NumericEntry', rtk.Entry)
NumericEntry.register{
    value = rtk.Attribute{
        calculate=function(self, attr, value, target)
            return value:gsub('%D','')
        end,
        set=function(self, attr, value, calculated, target)
            self.calc[attr] = calculated
            -- Pick one of the below:
            -- Option 1 - simple/cheap proxy
            self.numvalue = tonumber(calculated) or 0
            -- Option 2 - full fledged attr (but still read-only/one-way)
            self:attr('numvalue', tonumber(calculated) or 0)
        end,
    }
}
Here value's calculate function still filters out non-digits as before, but the setter updates the numeric form.

As I'm typing this, I recognize the documentation is really weak on this in terms of order of operations. When does calculate() get called? When does set() get called? When does _handle_attr() get called? (They actually get called in that order when attr() is invoked.) I'll look to improve this.

Last edited by tack; 05-14-2022 at 08:44 AM.
tack is offline   Reply With Quote
Old 05-14-2022, 05:00 PM   #186
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

rtk is returning what appears to be an incorrect value.

I am attempting to accomplish what I imagine will be a common UX with rtk Popup: if it's opened, I want Escape to close it rather than the Window it was spawned in. The problem is that Popup.opened always returns false for me.


Code:
local window = rtk.Window
local popup = rtk.Popup
window.onkeypresspost = function(event)
  if popup.opened and not event.handled and event.keycode == rtk.keycodes.ESCAPE then
    event:set_handled(self)
    popup.blur()
  end
end
window:open()
popup:open()
Press escape and nothing happens. If I log popup.opened inside the event handler, I get false back even though I can see the popup is open. I even tried logging popup.opened on an onclick on a button inside the popup and that returns false as well, which has got to be a bug unless I'm not understanding this attribute correctly.
MonkeyBars is offline   Reply With Quote
Old 05-14-2022, 05:25 PM   #187
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
The problem is that Popup.opened always returns false for me.
Yeah that does look like a legit bug. I seem to have carefully defined and documented the 'opened' attribute, but somehow failed to actually implement it.

Thanks for the example code. It needed a few fixes, but I appreciate the effort.

The best way to implement what you're after is to add an onkeypress handler to the popup itself, rather than using onkeypresspost at the window level:

Code:
    local window = rtk.Window()
    local popup = rtk.Popup()
    popup.onkeypress = function(self, event)
        if not event.handled and event.keycode == rtk.keycodes.ESCAPE then
            event:set_handled(self)
            popup:close()
        end
    end
    window:open()
    popup:open()

Last edited by tack; 05-14-2022 at 06:46 PM.
tack is offline   Reply With Quote
Old 05-14-2022, 05:47 PM   #188
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by tack View Post
Yeah that does look like a legit bug. I seem to have carefully defined and documented the 'opened' attribute, but somehow failed to actually implement it.
Now fixed in master.

BTW MonkeyBars I also pushed some fixes for the image loading/recoloring earlier today. If you're able to give the latest dev build a try, I'd be interested to know if that's working for you now as expected, without the need to call rtk.add_image_search_path().

Still working on implementation for relative min/max attributes. It's ended up needing a nontrivial amount of refactoring so I don't have anything committed there yet.
tack is offline   Reply With Quote
Old 05-14-2022, 07:54 PM   #189
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
The best way to implement what you're after is to add an onkeypress handler to the popup itself, rather than using onkeypresspost at the window level
I tried this at first but was missing the set_handled() call, thanks! Perfect

Quote:
Originally Posted by tack View Post
Now fixed in master.
Indeed it is! Not that I need it anymore

Quote:
I'd be interested to know if that's working for you now as expected, without the need to call rtk.add_image_search_path().
Well done – button icons working as expected now, thank you so much. I imagine the QA is helping iron out some low-hanging fruit for you and I'm only too glad to be filling that role for you to make rtk even better for the community.

Quote:
Still working on implementation for relative min/max attributes. It's ended up needing a nontrivial amount of refactoring so I don't have anything committed there yet.
Sounds intriguing!
MonkeyBars is offline   Reply With Quote
Old 05-14-2022, 08:05 PM   #190
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Ah actually I think I know what was happening before trying to define the Escape key behavior: if a widget inside the popup has focus, then the Window detects the keypress and closes. I mistook this behavior as meaning I needed to disable Window escape (though I didn't want to).

So I guess this is an event bubbling issue. I would expect that the keypress event ought to be caught by the Popup object since it has a lower node in the tree than Window and therefore would catch the keypress event before Window.

How do I ensure the Popup's contained widgets don't pass events all the way up to the Window?

Last edited by MonkeyBars; 05-15-2022 at 08:18 AM.
MonkeyBars is offline   Reply With Quote
Old 05-14-2022, 08:53 PM   #191
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Okay I think I may have honed into a bug here that may or may not be related to the previous post's issue: on first click onto a widget inside the Popup, the Popup fires a blur event. Subsequent clicks on contained widgets do not fire this event, and my onblur handler does fire (again) on actual blur. Edit: I could've sworn I was getting a blur event on actual blur before, but now it's not firing anymore – only on first child focus.

As an aside, I'd prefer to attach my function to the Popup close event (onclose) but you haven't provided such a handler.

Last edited by MonkeyBars; 05-14-2022 at 09:31 PM.
MonkeyBars is offline   Reply With Quote
Old 05-15-2022, 09:06 AM   #192
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
I imagine the QA is helping iron out some low-hanging fruit for you and I'm only too glad to be filling that role for you to make rtk even better for the community.
Some not-so-low hanging fruit too. I certainly appreciate the extra eyeballs, and it exposes corners of rtk that I'm not exercising much (or at all) in my own apps.

rtk has long passed the point where a proper test harness is needed. I need to invest the time in unit testing and render testing (a la Acid2/Acid3). It's not just terribly exciting work. (Although automated regression testing for render bugs is actually an interesting problem to solve.)


Quote:
Originally Posted by MonkeyBars View Post
So I guess this is an event bubbling issue. I would expect that the keypress event ought to be caught by the Popup object since it has a lower node in the tree than Window and therefore would catch the keypress event before Window.
I understand your intuition here. The reason it's not working the way you expect is that (unhandled) keypress events are dispatched only to focused widgets, and focus is only held by exactly one widget. That is, focus isn't a hierarchy: if a container holds rtk.Entry that is focused, the container itself is not considered to be focused.

The HTML analog here is Document.activeElement (not that rtk always follows DOM/CSS idioms -- I'm open to doing different things where it makes sense), which means that once you click on a widget inside the popup, the popup widget loses focus (so onblur gets fired on the popup) and the other widget takes focus (so onfocus gets fired on that widget).

So it's not a bug as such, it's working as designed. The question is whether the design is right.

The keypresspre and keypresspost events on rtk.Window are different in that they are fired on the window regardless of what's focused. Global catch-alls -- blunt force instruments.

I'll play with the idea of redefining the semantics of keypress to also dispatch to container widgets that contain the focused widget (provided the event isn't already handled).

Quote:
Originally Posted by MonkeyBars View Post
As an aside, I'd prefer to attach my function to the Popup close event (onclose) but you haven't provided such a handler.
This is easy enough to add. I'll do that today.

Edit: committed to master.

Last edited by tack; 05-15-2022 at 09:17 AM.
tack is offline   Reply With Quote
Old 05-15-2022, 09:31 AM   #193
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
rtk has long passed the point where a proper test harness is needed. I need to invest the time in unit testing and render testing (a la Acid2/Acid3). It's not just terribly exciting work. (Although automated regression testing for render bugs is actually an interesting problem to solve.)
Yes I actually considered LUAUnit for Superglue, but indeed the testing part was quite a conundrum, since sometimes it's not only hard to define the desired outcome, but also hard to detect.

Quote:
So it's not a bug as such, it's working as designed. The question is whether the design is right.
Yes this is why I was careful to phrase it as my intution rather than buggy behavior like a missing attribute

Quote:
I'll play with the idea of redefining the semantics of keypress to also dispatch to container widgets that contain the focused widget (provided the event isn't already handled).
Cool, keep us up to date! I think it may be more useful for devs to have an event object hierarchy for fine-tuning handling.

Quote:
This is easy enough to add. I'll do that today.

Edit: committed to master.
Sweet! I got my code working saving the Popup form values with onmouseup but this will be a lot more efficient.

Last edited by MonkeyBars; 05-15-2022 at 11:38 AM.
MonkeyBars is offline   Reply With Quote
Old 05-17-2022, 05:05 PM   #194
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by MonkeyBars View Post
how do I set window width to a percentage of screen width?
I used this code that X-Raym generously recommended. Works perfectly.

Code:
screen_left, screen_top, screen_right, screen_bottom = reaper.JS_Window_MonitorFromRect(0, 0, 0, 0, false)
Also, I wanted to share my volume curve code adapted from Cohler's fade curve functions. It seems very similar to the native send volume slider curve. I'm not actually using all of the code but didn't bother extracting unused code here:

Code:
reaperFade1 = function(x,c) return c<0 and (1+c)*x*(2-x)-c*(1-(1-x)^8)^.5 or (1-c)*x*(2-x)+c*x^4 end
reaperFade2 = function(x,c) return c<0 and (1+c)*x-c*(1-(1-x)^2) or (1-c)*x+c*x^2 end
reaperFade3 = function(x,c) return c<0 and (1+c)*x-c*(1-(1-x)^4) or (1-c)*x+c*x^4 end
reaperFadeg = function(x,t) return t==.5 and x or ((x*(1-2*t)+t^2)^.5-t)/(1-2*t) end
reaperFadeh = function(x,t) local g = reaperFadeg(x,t); return (2*t-1)*g^2+(2-2*t)*g end

reaperFadeIn = {
  function(x,c) c=c or 0; return reaperFade3(x,c) end,
  function(x,c) c=c or 0; return reaperFade1(x,c) end,
  function(x,c) c=c or 1; return reaperFade2(x,c) end,
  function(x,c) c=c or -1; return reaperFade3(x,c) end,
  function(x,c) c=c or 1; return reaperFade3(x,c) end,
  function(x,c) c=c or 0; local x1 = reaperFadeh(x,.25*(c+2)); return (3-2*x1)*x1^2 end,
  function(x,c) c=c or 0; local x2 = reaperFadeh(x,(5*c+8)/16); return x2<=.5 and 8*x2^4 or 1-8*(1-x2)^4 end,
}

reaperFade = function(ftype,t,s,e,c,inout)
  --
  -- Returns 0 to 1
  --
  -- ftype is the REAPER fade type 1-7 (Note: not 0-6 here)
  -- t - time code where fade is calculated
  -- s - time code of fade start
  -- e - time code of fade end
  -- c - REAPER curvature parameter (D_FADEINDIR, D_FADEOUTDIR)
  -- inout - true for fade in, false for fade out
  --
  if e<=s then return 1 end
  t = t<s and s or t>e and e or t
  local x = (t-s)/(e-s)
  local ret = reaperFadeIn[ftype](table.unpack(inout and {x,c} or {1-x,-c}))
  return ret * 4
end

function getAPIVolume(slider_value)

  return reaperFade(5, slider_value, 0, 4, 1, true)
end

-- volume slider at 0.00dB = slider internal value of 2.8285 on standard Reaper volume scale of 0 to 4

Last edited by MonkeyBars; 05-17-2022 at 08:01 PM.
MonkeyBars is offline   Reply With Quote
Old 05-17-2022, 05:51 PM   #195
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Hey there tack!

I'm getting what appears to be a shrinkwrap margin bug with minw. This actually doesn't affect me since I can use w just as well, but I wanted to report it.

w = screen width * 0.4:


minw = screen width * 0.4:
MonkeyBars is offline   Reply With Quote
Old 05-17-2022, 07:10 PM   #196
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,619
Default

Quote:
Originally Posted by MonkeyBars View Post
I used this code that X-Raym generously recommended. Works perfectly.

screen_left, screen_top, screen_right, screen_bottom = reaper.JS_Window_MonitorFromRect(0, 0, 0, 0, false)
Some thoughts:
  1. JS_Window_MonitorFromRect() is deprecated and the recommendation is to use JS_Window_GetViewportFromRect() instead
  2. This only gets the screen width of the primary display (which may be what you want, just pointing it out)
  3. If you fetch the latest dev build of rtk, you hopefully won't need to do this anymore

I just committed an implementation for relative min/max sizes. Also, if rtk.Window.x and y are nil when open() is called (which I've decided to make default), then the window will auto-center on the primary display.

This is a pretty big and invasive commit and the probability of regression is high. If you're able, I'd love to get your feedback on it. Just back up your current rtk.lua first.

Quote:
Originally Posted by MonkeyBars View Post
Also, I wanted to share my volume curve code adapted from Cohler's fade curve functions. It seems very similar to the native send volume slider curve.
I was actually going to tackle a transformation function matching REAPER's faders but looking at this code I'm glad I don't have to. Thanks for sharing.

Quote:
Originally Posted by MonkeyBars View Post
I'm getting what appears to be a shrinkwrap margin bug with minw. This actually doesn't affect me since I can use w just as well, but I wanted to report it.
It's entirely possible this bug has gone away when the recent changes to support relative min/max. If you're able to try out latest dev build, please do let me know.
tack is offline   Reply With Quote
Old 05-17-2022, 08:08 PM   #197
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by tack View Post
I just committed an implementation for relative min/max sizes.
Awesome! How do I use it? w = 0.4 works perfectly now, but minw = 0.4 gives me a result more like w = 0.95. Where is Window getting its (auto) width from? (This could be desired behavior for all I know and I'm interested to find out the logic here.)

Quote:
Also, if rtk.Window.x and y are nil when open() is called (which I've decided to make default), then the window will auto-center on the primary display.
Works great!

Quote:
I was actually going to tackle a transformation function matching REAPER's faders but looking at this code I'm glad I don't have to. Thanks for sharing.
Sure thing! I found switching around the curve numbers didn't change much but feel free to find the one you like best and axe the other formulae.

Quote:
It's entirely possible this bug has gone away when the recent changes to support relative min/max. If you're able to try out latest dev build, please do let me know.
Hm, not sure here because the window goes so very wide with minw, but certainly I don't see the bottom margin issue anymore.

Last edited by MonkeyBars; 05-17-2022 at 08:51 PM.
MonkeyBars is offline   Reply With Quote
Old 05-19-2022, 08:41 PM   #198
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Is there a way to change the color of a checkbox? I would think color would be the right attr, but it doesn't do anything.
MonkeyBars is offline   Reply With Quote
Old 05-20-2022, 02:47 AM   #199
dri_ft
Human being with feelings
 
Join Date: Aug 2019
Posts: 22
Default

I spent a little time on implementing a NumberEntry and I have something that's good enough for my purposes. I'll post it here in case anyone is interested to see or use it:

Code:
package.path = reaper.GetResourcePath() .. '/Scripts/rtk/1/?.lua'
local rtk = require('rtk')

NumberEntry = rtk.class('NumberEntry', rtk.Entry)
NumberEntry.register {
	dragscale = rtk.Attribute { default=0.1 },
	-- other attributes not mentioned here because they have nothing to set here, not even a default: min, max
}
function NumberEntry:initialize(attrs, ...)
	attrs.value = tostring(attrs.numvalue)
	rtk.Entry.initialize(self, attrs, ...)
	self.valuesynced = true -- whether numvalueue and value are in sync; we keep track of this mainly so we know whether to flash to indicate update on blur
end
function NumberEntry:_setnumvalue(num)
	num = math.round(rtk.clamp(num, self.min, self.max))
	self.numvalue = num
	self:sync('value', tostring(num))
	self.valuesynced = true
end
function NumberEntry:_handle_mousewheel(ev)
	self:_setnumvalue(self.numvalue - ev.wheel)
end
do
	local dragstart, dragstartval, dragoldval
	function NumberEntry:ondragstart(entry, ev)
		dragstart, dragstartval, dragoldval = ev.y, self.numvalue, self.numvalue
	end
	function NumberEntry:ondragmousemove(ev)
		local num = dragstartval + (dragstart - ev.y) * self.dragscale
		if num ~= dragoldval then
			self:_setnumvalue(num)
			dragoldval = num
		end
	end
end
function NumberEntry:_handle_keypress(ev)
	if ev.keycode == rtk.keycodes.ENTER then
		local num = tonumber(self.value)
		if num then
			self:_setnumvalue(num)
			self:animate { 'bg', src='steelblue', dst=rtk.Attribute.DEFAULT, duration=0.4 }
		else
			self:animate { 'bg', src='maroon', dst=rtk.Attribute.DEFAULT, duration=0.4 }
		end
	else
		rtk.Entry._handle_keypress(self, ev)
		self.valuesynced = false
	end
end
function NumberEntry:_handle_blur(ev)
	if not self.valuesynced then
		local num = tonumber(self.value)
		if num then
			self:_setnumvalue(num)
			self:animate { 'bg', src='steelblue', dst=rtk.Attribute.DEFAULT, duration=0.4 }
		else
			self:animate { 'bg', src='maroon', dst=rtk.Attribute.DEFAULT, duration=0.4 }
			self:sync('value', tostring(self.numvalue))
			self.valuesynced = true
		end
	end
end
And to demo:

Code:
local window = rtk.Window()
local box = window:add(rtk.HBox())

local numentry = box:add(NumberEntry { numval=5, max=12, min=-5, textwidth=5 })
box:add(NumberEntry { numval=105, max=112, min=95, textwidth=5 })
function numentry:onchange(event)
	reaper.ShowConsoleMsg(string.format('changed numval: %s\n', self.numval))
end

window:open { align='center' }
Its value can be changed by scrolling over it, dragging it vertically, or typing a number. It only updates numvalue from the typed contents on a return keypress or on losing focus (blur), to avoid it being updated to 1 in the process of typing '100'. It also takes a min and max which will constrain the value.

It feels silly to 'licence' such a trivial piece of code, but for the record I'll say that I grant anyone to use it however they will, in whatever kind of project. (Tack, if you feel like using it as a starting point for a NumberEntry widget in rtk proper then feel free.)

A couple of TODOs:
  • I'd like to add a custom step size; right now it only scrolls and drags in increments of 1 (which is fine for my current project).
  • In a way it would be semantically nicer to only call onchange() when numvalue is changed (instead of for each keypress while typing in a number) but I couldn't figure out how to do this and maybe it can't be easily done. Doesn't greatly matter.
  • I feel a bit like _setnumvalue() (or parts of it?) should be in a set or calculate function on the numvalue attribute, but this gets ugly because it also updates value as a side effect and doing this from within set() caused trouble. Any thoughts, tack?
  • I mentioned before that I'd use right-align if Entry allowed it - no hurry on that, but I'll also mention that it would be cool to be able to set the type of font in very broad terms without naming specifics, i.e. 'mono' here, but also possibly 'sans' or 'serif'. But if that's a lot of extra work for you it probably isn't worth it.
dri_ft is offline   Reply With Quote
Old 05-31-2022, 08:16 AM   #200
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Thanks dri_ft! I'll probably use your widget on my next project.

Just a quick note to thank tack for rtk's well thought-out image scaling system. I just used density for the first time and it works great in combination with Reaper UI scaling. Nicely done sir!
MonkeyBars is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 05:42 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.