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

Reply
 
Thread Tools Display Modes
Old 05-31-2022, 02:28 PM   #201
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by dri_ft View Post
I spent a little time on implementing a NumberEntry and I have something that's good enough for my purposes.
It remains on my to-do list to give this a proper look through. Sorry, not meaning to give you the cold shoulder, just been very busy on other things the past couple weeks. I appreciate the contribution!

More generally -- and this applies to anyone -- it'd be really helpful to log feature requests (or nontrivial bugs too) at https://github.com/jtackaberry/rtk/issues . I try to create issues from comments here but I'm not terribly disciplined.

That said, tracking halign attr support for rtk.Entry here: https://github.com/jtackaberry/rtk/issues/13


Quote:
Originally Posted by MonkeyBars View Post
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.
That's a perfectly cromulent assumption, but, yeah, not implemented yet. It's actually not as trivial to do as I'd like. Tracking at https://github.com/jtackaberry/rtk/issues/12

Quote:
Originally Posted by MonkeyBars View Post
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!
Great to hear! Thanks for the feedback.
tack is online now   Reply With Quote
Old 06-07-2022, 07:26 AM   #202
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Hello tack!

I'm not exactly sure when this happened, but somehow my Viewport stopped expanding to its child height. I am using build 1.2.0-40-g2c31150-dev and I assume it must have started with this version as it was expanding properly at some point.

The child in question, "content", is a VBox with 2 children. Interestingly, the Viewport does not cut off between the children, but chops the height within the area of the top "content" child.

I think perhaps the window in general is loading with default height or something...

code basics (doesn't include the population of the target tracks box but they're just checkboxes in there):

Code:
_routing_options_objs = {
    ["window"] = rtk.Window{title = "MB_Buss Driver - Batch add or remove send(s) or receive(s) on selected track(s)", w = 0.4, maxh = rtk.Attribute.NIL},
    ["viewport"] = rtk.Viewport{halign = "center", bpadding = 5, border = "1px orange"},
    ["brand"] = rtk.VBox{halign = "center", padding = "2 2 1", border = "1px #878787", bg = "#505050"},
    ["title"] = rtk.Heading{"Buss Driver", fontscale = "0.6"},
    ["logo"] = rtk.ImageBox{rtk.Image():load(_logo_img_path), w = 47, halign = "center", margin = "1 0"},
    ["configure_btn_wrapper"] = rtk.Container{w = 1, halign = "right", margin = "5 3 0 0"},
    ["configure_btn"] = rtk.Button{label = "Configure send settings", tooltip = "Pop up routing settings to be applied to all sends or receives created", padding = "4 5 6", fontscale = 0.67},
    ["content"] = rtk.VBox{halign = "center", padding = "10 0 0"},
    ["action_sentence_wrapper"] = rtk.Container{w = 1, halign = "center"},
    ["action_sentence"] = rtk.HBox{valign = "center", tmargin = 9},
    ["action_text_start"] = rtk.Text{"I want to "},
    ["addremove_wrapper"] = rtk.VBox{margin = "0 5"},
    ["add_checkbox"] = rtk.CheckBox{"add +", h = 17, fontscale = 0.925, margin = "0 5 1 2", padding = "0 2 3 2", spacing = 3, valign = "center", value = true, ref = "add_checkbox"},
    ["remove_checkbox"] = rtk.CheckBox{"remove -", h = 17, fontscale = 0.925, margin = "0 5 1 2", padding = "0 2 3 2", spacing = 3, valign = "center", ref = "remove_checkbox"},
    ["type_wrapper"] = rtk.VBox{rmargin = 5},
    ["send_checkbox"] = rtk.CheckBox{"sends", h = 17, fontscale = 0.925, margin = "0 5 1 2", padding = "0 2 3 2", spacing = 3, valign = "center", value = true, ref = "send_checkbox"},
    ["receive_checkbox"] = rtk.CheckBox{"receives", h = 17, fontscale = 0.925, margin = "0 5 1 2", padding = "0 2 3 2", spacing = 3, valign = "center", ref = "receive_checkbox"},
    ["action_text_end"] = rtk.Text{" to the selected tracks."},
    ["target_tracks_subheading"] = rtk.Text{"Which tracks do you want to add sends to?", w = 1, tmargin = 14, fontscale = 0.95, fontflags = rtk.font.BOLD, halign = "center", fontflags = rtk.font.BOLD},
    ["form_fields"] = rtk.VBox{padding = "10 10 5", spacing = 10},
    ["form_bottom"] = rtk.Container{w = 1, margin = 10},
    ["form_buttons"] = rtk.HBox{spacing = 10},
    ["save_options_wrapper"] = rtk.HBox{tmargin = 5},
    ["save_options"] = rtk.CheckBox{"Save choices & settings on close", h = 17, padding = "0 2 3 2", spacing = 3, valign = "center", fontscale = 0.67, color = "#bbbbbb", textcolor2 = "#bbbbbb", ref = "save_options_checkbox"},
    ["form_submit"] = rtk.Button{"Add", disabled = true},
    ["form_cancel"] = rtk.Button{"Cancel"},
    ["reset_wrapper"] = rtk.HBox{valign = "center"},
    ["reset_btn"] = rtk.Button{"Reset all options", tooltip = "Return all tracks and settings to initial state", padding = "4 5 6", color = "#8A4C00R", fontscale = 0.67, textcolor = "#D6D6D6"}
  }

_routing_options_objs.addremove_wrapper:add(_routing_options_objs.add_checkbox)
  _routing_options_objs.addremove_wrapper:add(_routing_options_objs.remove_checkbox)
  _routing_options_objs.type_wrapper:add(_routing_options_objs.send_checkbox)
  _routing_options_objs.type_wrapper:add(_routing_options_objs.receive_checkbox)
  _routing_options_objs.action_sentence:add(_routing_options_objs.action_text_start)
  _routing_options_objs.action_sentence:add(_routing_options_objs.addremove_wrapper)
  _routing_options_objs.action_sentence:add(_routing_options_objs.type_wrapper)
  _routing_options_objs.action_sentence:add(_routing_options_objs.action_text_end)
  _routing_options_objs.action_sentence_wrapper:add(_routing_options_objs.action_sentence)
  _routing_options_objs.form_fields:add(_routing_options_objs.action_sentence_wrapper)
  _routing_options_objs.form_fields:add(_routing_options_objs.target_tracks_subheading)
  _routing_options_objs.form_fields:add(_routing_options_objs.target_tracks_box)
  _routing_options_objs.save_options_wrapper:add(_routing_options_objs.save_options)
  _routing_options_objs.form_buttons:add(_routing_options_objs.form_submit)
  _routing_options_objs.form_buttons:add(_routing_options_objs.form_cancel)
  _routing_options_objs.reset_wrapper:add(_routing_options_objs.reset_btn)
  _routing_options_objs.form_bottom:add(_routing_options_objs.save_options_wrapper, {halign = "left"})
  _routing_options_objs.form_bottom:add(_routing_options_objs.form_buttons, {halign = "center"})
  _routing_options_objs.form_bottom:add(_routing_options_objs.reset_wrapper, {halign = "right"})
  _routing_options_objs.content:add(_routing_options_objs.form_fields)
  _routing_options_objs.content:add(_routing_options_objs.form_bottom)
  _routing_options_objs.viewport:attr("child", _routing_options_objs.content)
  _routing_options_objs.configure_btn_wrapper:add(_routing_options_objs.configure_btn)
  _routing_options_objs.brand:add(_routing_options_objs.title)
  _routing_options_objs.brand:add(_routing_options_objs.logo)
  _routing_options_objs.window:add(_routing_options_objs.configure_btn_wrapper)
  _routing_options_objs.window:add(_routing_options_objs.brand)
MonkeyBars is offline   Reply With Quote
Old 06-07-2022, 08:30 AM   #203
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by MonkeyBars View Post
I'm not exactly sure when this happened, but somehow my Viewport stopped expanding to its child height. I am using build 1.2.0-40-g2c31150-dev and I assume it must have started with this version as it was expanding properly at some point.
Can you fetch the latest dev build and confirm the problem exists there as well? If so I'll dig in a bit further this evening, and will see how I can adapt your example to make it work. (It's a big help if the test cases are fully baked and copy-pastable to reproduce the issue.)

BTW, you may find it a bit easier to construct your widget hierarchy inline and use the 'ref' attr to reference widgets (which you only need to do for widgets you actually do need to access after initial construction). This way the relationship between the widgets is clearer through indentation. Like:

Code:
local w = 
  rtk.Window{title='Whatever', w=0.4, maxh=rtk.Attribute.NIL,
    rtk.Viewport{ref='viewport', halign='center', bpadding=5, border='1px orange',
      rtk.VBox{halign='center', padding='2 2 1',
        rtk.Heading{'Buss Driver', fontscale=0.6},
        rtk.ImageBox{_logo_img_path, w=47},
        rtk.Container{
          rtk.Button{ref='configure_btn', 'Configure send settings', onclick=configure},
          ...
        } -- Container
      } -- VBox
    } -- Viewport
  } -- Window
w:open()
-- Obviously contrived, but you get the idea.
w.refs.viewport:scrollto(0, 0)
When constructing the graph this way, cell attributes that you normally provide to rtk.Container:add() can be implicitly set as a 'cell' attribute on the widget, which is a table of cell attributes that its parent container will use. Like:

Code:
   rtk.Text{'Foo', cell={halign='right'}}
tack is online now   Reply With Quote
Old 06-07-2022, 10:23 AM   #204
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 fetch the latest dev build and confirm the problem exists there as well?
The new build does not solve my issue, but I see the Viewport scrollbar appears by default now which is very cool.

Quote:
(It's a big help if the test cases are fully baked and copy-pastable to reproduce the issue.)
DM'd ya!

Quote:
BTW, you may find it a bit easier to construct your widget hierarchy inline and use the 'ref' attr to reference widgets
Cool thanks, I'll try to give that a shot in future
MonkeyBars is offline   Reply With Quote
Old 06-10-2022, 10:23 PM   #205
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Really weird, at some point the height bug went away! I was paying attention to other stuff and don't know at what point the Viewport began expanding to its child height again. I'll let you know if and when it pops up again, but for now you're off the hook! Thanks.
MonkeyBars is offline   Reply With Quote
Old 06-11-2022, 05:42 PM   #206
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Okay now I'm getting a different bug. I made a new FlowBox and am using hspacing to add margin between the Text children. At first it seemed okay I think (?) but now the spaces are very uneven:



Code:
selected_tracks_list = rtk.FlowBox{hspacing = 10, border = "1px green"}

selected_tracks_list:add(rtk.Text{this_track_num .. ": " .. this_track_name, fontscale = 0.67, color = "#D6D6D6", border = "1px orange"})
MonkeyBars is offline   Reply With Quote
Old 06-11-2022, 05:52 PM   #207
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by MonkeyBars View Post
Okay now I'm getting a different bug. I made a new FlowBox and am using hspacing to add margin between the Text children. At first it seemed okay I think (?) but now the spaces are very uneven:
At first blush this looks ok to me. FlowBox ensures that all cells have the same width. If you set debug=true on the FlowBox it will show the cell boundaries.

(Pro tip: if you set rtk.log.level to rtk.log.DEBUG then you can hit F12 to toggle dynamic layout debug where mousing over a widget will paint it with debug.)

Edit: now that I bust out the measurement tool, it looks like the last cell is 12px or so smaller than the other cells. Probably off by hspacing + lborder + rborder. That part's definitely a bug, but I don't think it's what you meant. I imagine you were expecting variable sized columns like you might get with an HTML table, but FlowBox is intentionally consistent width because then the reflow cost is O(n) with respect to number of children, as opposed to AFAICT what would be O(n^2) (or possibly worse) with best-fit variable-sized columns.

Last edited by tack; 06-11-2022 at 06:05 PM.
tack is online now   Reply With Quote
Old 06-11-2022, 07:22 PM   #208
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
(Pro tip: if you set rtk.log.level to rtk.log.DEBUG then you can hit F12 to toggle dynamic layout debug where mousing over a widget will paint it with debug.)
Hey that's sick, thanks!

Quote:
FlowBox is intentionally consistent width because then the reflow cost is O(n) with respect to number of children, as opposed to AFAICT what would be O(n^2) (or possibly worse) with best-fit variable-sized columns.
Ah I thought I was seeing variable column widths at some point but must've been a trick of the eye. Thanks for having a look – I can deal with this!
MonkeyBars is offline   Reply With Quote
Old 06-14-2022, 11:31 AM   #209
nscotto
Human being with feelings
 
Join Date: Jul 2020
Posts: 84
Default

Hi,

Can I use rtk.Image to wrap an image generated with JS_SLICE api?

I am using this code to capture a window plugin screenshot and would like to use trk.Image to manipulate it:

Code:
function CapWindowToPng(hwnd, filename, win10)
  -- TODO: parameterise cropping
  local cropleft, cropright, croptop, cropbottom = 0, 0, 60, 0
  local srcx,srcy = 0,0
  local srcDC = reaper.JS_GDI_GetWindowDC(hwnd)
  local _,_,w,h = GetBounds(hwnd)

  if win10 then srcx=7 w=w-14 h=h-7 end -- * Workaround for Win10 to ignore invisible window borders
  h = h - croptop - cropbottom
  w = w - cropleft - cropright
  srcx = srcx + cropleft
  srcy = srcy + croptop
  
  local destBmp = reaper.JS_LICE_CreateBitmap(true,w,h)
  local destDC = reaper.JS_LICE_GetDC(destBmp)
  -- copy source to dest & write PNG
  reaper.JS_GDI_Blit(destDC, 0, 0, srcDC, srcx, srcy, w, h)
  reaper.JS_LICE_WritePNG(filename, destBmp, false)
  -- clean up resources
  reaper.JS_GDI_ReleaseDC(hwnd, srcDC)
  reaper.JS_LICE_DestroyBitmap(destBmp)
end
nscotto is offline   Reply With Quote
Old 06-14-2022, 11:43 AM   #210
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by nscotto View Post
Can I use rtk.Image to wrap an image generated with JS_SLICE api?
REAPER doesn't let you load image buffers from memory (I really wish it did), so you'd have to save it to disk, and then you can load it back from disk with rtk.Image(). But I see you're writing out a png file in your code snippet, so you could load it in off disk (rtk.Image():load(filename)).

Not super efficient compared to just reading it directly from memory, but works within REAPER's own limitations (at least insofar as I understand them).
tack is online now   Reply With Quote
Old 06-15-2022, 01:05 AM   #211
nscotto
Human being with feelings
 
Join Date: Jul 2020
Posts: 84
Default

Quote:
Originally Posted by tack View Post
REAPER doesn't let you load image buffers from memory (I really wish it did), so you'd have to save it to disk, and then you can load it back from disk with rtk.Image(). But I see you're writing out a png file in your code snippet, so you could load it in off disk (rtk.Image():load(filename)).

Not super efficient compared to just reading it directly from memory, but works within REAPER's own limitations (at least insofar as I understand them).
Ok, thank you!
nscotto is offline   Reply With Quote
Old 06-16-2022, 09:52 AM   #212
nscotto
Human being with feelings
 
Join Date: Jul 2020
Posts: 84
Default

Hi, I have two questions:

1. is there a way to popup a file selector?

2. is there a widget similar to the plugin list, where the user can select possibly multiple elements from a long list?
nscotto is offline   Reply With Quote
Old 06-16-2022, 05:16 PM   #213
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by nscotto View Post
1. is there a way to popup a file selector?
Not out-of-the-box with rtk. Such a widget could be built. But if you're willing to have your script require the JS API, you could use JS_Dialog_BrowseForOpenFiles.

Quote:
Originally Posted by nscotto View Post
2. is there a widget similar to the plugin list, where the user can select possibly multiple elements from a long list?
No multi-select at the moment I'm afraid. rtk.OptionList is just single select. I am working on a List widget (as part of a ComboBox widget which I need for Reaticulate), so this will come, in a month or so, but nothing available at the moment.

It's possible with what's there now of course, but you'd have to sling it from first principles, using a VBox with child widgets representing the menu items, and adding onmouseenter/onmouseleave handlers to those widgets to update the bg attribute to show the currently selected item. Similar to how you might build such a thing using HTML/CSS.
tack is online now   Reply With Quote
Old 06-17-2022, 12:05 AM   #214
nscotto
Human being with feelings
 
Join Date: Jul 2020
Posts: 84
Default

Quote:
Originally Posted by tack View Post
Not out-of-the-box with rtk. Such a widget could be built. But if you're willing to have your script require the JS API, you could use JS_Dialog_BrowseForOpenFiles.

No multi-select at the moment I'm afraid. rtk.OptionList is just single select. I am working on a List widget (as part of a ComboBox widget which I need for Reaticulate), so this will come, in a month or so, but nothing available at the moment.

It's possible with what's there now of course, but you'd have to sling it from first principles, using a VBox with child widgets representing the menu items, and adding onmouseenter/onmouseleave handlers to those widgets to update the bg attribute to show the currently selected item. Similar to how you might build such a thing using HTML/CSS.
Thanks for the file selector!

I was thinking about making a multi-select, although I am relatively new to lua and not a webdev so it's a bit of a challenge...
There also need to be a scrollbar when the list is too long to be fully rendered.
nscotto is offline   Reply With Quote
Old 06-17-2022, 03:44 PM   #215
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by nscotto View Post
I was thinking about making a multi-select, although I am relatively new to lua and not a webdev so it's a bit of a challenge. There also need to be a scrollbar when the list is too long to be fully rendered.
I think the easiest thing based on the widgets available that implements a multi-select would be to just create a VBox with a bunch of checkboxes. You can put the box in a viewport to get the scrollbar.

This example maintains a table of selected names (keyed on the name).

Code:
local names = {'Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', selected={}}
local function select(item)
    names.selected[item.label] = not names.selected[item.label] or nil
    rtk.log.info('current selected: %s', table.tostring(names.selected))
end
local w = rtk.Window{
    rtk.Viewport{
        border='1px #ffffff22', padding=5, margin=10,
        rtk.VBox{ref='listbox', padding={2, 5}}
    }
}
for _, name in ipairs(names) do
    w.refs.listbox:add(rtk.CheckBox{name, onchange=select})
end
w:open()
tack is online now   Reply With Quote
Old 07-02-2022, 09:03 PM   #216
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
Really weird, at some point the height bug went away! I was paying attention to other stuff and don't know at what point the Viewport began expanding to its child height again. I'll let you know if and when it pops up again, but for now you're off the hook! Thanks.
Definitely getting intermittence on the Viewport height refreshing, as the bug came back.

I just added a :refresh() call on the Viewport right before the Window gets open()ed and it fixed it. So perhaps there ought to be some more default refreshes in the rtk codebase...?
MonkeyBars is offline   Reply With Quote
Old 07-03-2022, 02:25 AM   #217
dri_ft
Human being with feelings
 
Join Date: Aug 2019
Posts: 22
Default

Quote:
Originally Posted by nscotto View Post
is there a widget similar to the plugin list, where the user can select possibly multiple elements from a long list?
Oh, I have a not-quite-finished multiselect widget. I was holding off from posting it as the code is not particularly clean, but I post it here as it may be useful to you. (Sorry, I only just saw your post or would've posted this sooner.)

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

ListItem = rtk.class('ListItem', rtk.Text)
ListItem.register {
    selected = rtk.Attribute {
        set = function(self, attr, value, calculated, target)
            --self.calc[attr] = calculated -- uh, do we need this?
			self:attr('bg', value and rtk.theme.accent_subtle or nil)
        end,
    },
    _focused = rtk.Attribute { -- TODO better name! (focused is reserved)
        set = function(self, attr, value, calculated, target)
            --self.calc[attr] = calculated -- uh, do we need this?
			self:attr('border',  value and { rtk.theme.accent, 1 } or nil)
			self:attr('padding', value and 3 or 4)
        end,
    },
}
function ListItem:_handle_click(ev)
    if rtk.Text._handle_click(self, event) == false then return false end
	self.parent:_handle_item_click(self, ev)
    return true
end

local ListBox = rtk.class('ListBox', rtk.VBox)
-- TODO register selected and _focused fields; should be possible to set these programmatically, and have that reflected in the gui, I guess?
function ListBox:initialize(attrs, ...)
	items = attrs and table.remove(attrs, 1) or {}
	-- TODO should items be passable-in as attrs.items as well as attrs.items?
	rtk.VBox.initialize(self, attrs, ...)
	for _,item in ipairs(items) do
		self:add(ListItem { item, wrap=false, padding=4, w=1 })
	end
	-- TODO set up selected and _focused fields (but should these be the items or their values? and should they be settable by the items or their values? and should they have values different to their labels?)
end
function ListBox:focusitem(item)
	for _,li_a in ipairs(self.children) do li_a[1]:attr('_focused', false) end
	item:attr('_focused', true)
	self:attr('_focused', item)
end
function ListBox:select(value, trigger) -- TODO
	--return self:attr('selected', value, trigger)
end
function ListBox:_handle_item_click(item, ev)
	-- TODO maintain self.selected (table/list)
	if gfx.mouse_cap & 8--[[shift]] > 0 then -- select range
		local inrange = coroutine.create(function(li)
			while li ~= self._focused and li ~= item do li = coroutine.yield(false) end li = coroutine.yield(true)
			while li ~= self._focused and li ~= item do li = coroutine.yield(true)  end      coroutine.yield(true)
			while true do coroutine.yield(false) end
		end)
		-- li_a is table of listitem and attrs
		for _,li_a in ipairs(self.children) do
			local li = li_a[1]
			local _, inrange = coroutine.resume(inrange, li)
			li:attr('selected', inrange)
		end
	else
		self:focusitem(item)
		if gfx.mouse_cap & 4--[[ctrl]] > 0 then item:attr('selected', not item.selected)
		else
			for _,li_a in ipairs(self.children) do li_a[1]:attr('selected', false) end
			item:attr('selected', true)
		end
	end
end
And to test:
Code:
local window = rtk.Window()
local lb = window:add(ListBox { { 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vw', 'xyz' } })
window:open { align='center' }
Again, I release the code for anyone to do with what they wish.

It doesn't include scroll behaviour; to get that you would want to wrap it in a Viewport. It does include multiple selection with ctrl and shift, and it has separate concepts of 'selected' (can be multiple items) and 'focused' (can only be one item, the last one clicked.

The listbox itself doesn't keep track of which of its items are selected/focused, but you can check the selected/focused attributes on its children (ListItems), to see what state they're in - except that the 'focused' attribute is reserved for use by rtk, so it's named as _focused here (not a particularly good name; feel free to change it).

So it works to that limited extent, but I admit I'm not really sure how useable it is. I can't really offer support on it but let me know, at least, whether you can get it to work and how you get on with using it.
dri_ft is offline   Reply With Quote
Old 07-03-2022, 10:11 AM   #218
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

I just deployed my second script, which features a lot more intensive use of our favorite GUI library for Reaper, rtk! It's a bulk routing add/remove utility called Buss Driver.
MonkeyBars is offline   Reply With Quote
Old 07-05-2022, 10:47 AM   #219
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by MonkeyBars View Post
I just deployed my second script, which features a lot more intensive use of our favorite GUI library for Reaper, rtk! It's a bulk routing add/remove utility called Buss Driver.
Awesome little tool! Congrats on the release.
tack is online now   Reply With Quote
Old 07-25-2022, 08:07 AM   #220
fricia
Human being with feelings
 
Join Date: Apr 2017
Posts: 119
Default Tabbing through entry widgets

Firstly: Thank you so much tack this is sick!

Right now I'm trying to implement tabbing/shift+tabbing through multiple entry widgets. I've looked through the documentation and it looks like it's possible to do with the onkeypress event handler. Would this be the intended way or is there a much easier way that I'm not seeing?
fricia is online now   Reply With Quote
Old 07-25-2022, 06:56 PM   #221
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by fricia View Post
Right now I'm trying to implement tabbing/shift+tabbing through multiple entry widgets. I've looked through the documentation and it looks like it's possible to do with the onkeypress event handler. Would this be the intended way or is there a much easier way that I'm not seeing?
That'd be the way, onkeypress and check for event.keycode == rtk.keycodes.TAB, but you know I've got a good chunk of this logic already implemented in my local dev tree, for generic tab/shift-tab supported throughout the whole window. I've been sitting on this code for far too long.

If you're ok to wait until the weekend, I can get this committed so you can at least test it out and provide feedback?
tack is online now   Reply With Quote
Old 07-25-2022, 11:26 PM   #222
fricia
Human being with feelings
 
Join Date: Apr 2017
Posts: 119
Default

Quote:
Originally Posted by tack View Post
That'd be the way, onkeypress and check for event.keycode == rtk.keycodes.TAB, but you know I've got a good chunk of this logic already implemented in my local dev tree, for generic tab/shift-tab supported throughout the whole window. I've been sitting on this code for far too long.

If you're ok to wait until the weekend, I can get this committed so you can at least test it out and provide feedback?
That would be amazing! There's absolutely no hurry, as I'm gonna be away all of next week anyway. But after that I'd love to do some testing.

Thank you!
fricia is online now   Reply With Quote
Old 07-29-2022, 08:07 AM   #223
fricia
Human being with feelings
 
Join Date: Apr 2017
Posts: 119
Default

So after working a couple days with it I'm slowly starting to get a grip on programming with rtk. A couple of questions that have accumulated (besides the tabbing through entries question above):

1. I'm trying top implement resizing entry fields and I'm encountering the following:


Code:
local w = rtk.Window{title='TESTING', w=600, h=90}

local hBox = w:add(rtk.HBox{padding='15 0'})
local e1 = hBox:add(rtk.Entry{w=110, h=30, placeholder='entry'})
local resize = hBox:add(rtk.Spacer{w=10, h=30, bg='dodgerblue'})

w:reflow()
local currWidth=(e1.calc.w)

resize.onmouseenter = function (self, event)
  resize:attr('cursor', rtk.mouse.cursors.SIZE_EW)
end

resize.onmousemove = function (self, event)
  resize:attr('cursor', rtk.mouse.cursors.SIZE_EW)
end


resize.ondragstart = function(self, event)
  return event.x, nil
end

resize.ondragmousemove = function(self, event, dragarg)
    
 if not event.simulated then
    
    currWidth = rtk.mouse.x
    e1:attr('w',currWidth )
  end
end

w:open()
The end of the entry widget should always be at the mouse cursor. This is not how I would actually implement it, but it clearly shows the problem: The width the script is calculating and the width shown on the screen are not matching. The widget jumps everytime I start to drag and when dragging far distances cursor and widget end start to drift apart.

2. In practice I would also like to use expand on the entries, so that they grow/shring when resizing the window. So I change my widget initialization to:

Code:
...
local e1 = hBox:add(rtk.Entry{ h=30, placeholder='entry1'}, {expand=1, fillw=true})
local resize = hBox:add(rtk.Spacer{w=10, h=30, bg='dodgerblue'})
local e2 = hBox:add(rtk.Entry{ h=30, placeholder='entry2'}, {expand=1, fillw=true})
...


However, as you can see, as soon I set the width of e1 by draging, fillw doesn't work anymore. Can this be circumvented somehow?
I played around with modifying the expand value instead of the width, but this was quite a bad experience and (correct me if I'm wrong) it really doesn't seem like the inteded way.
And on a related note: Would there even be a quick way to get the expand behaviour (widget size follows window resizing) with a normal container?
fricia is online now   Reply With Quote
Old 07-29-2022, 08:21 AM   #224
fricia
Human being with feelings
 
Join Date: Apr 2017
Posts: 119
Default

Quote:
Originally Posted by tack View Post
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().
One more thing: This dev build seems to fix the image path problem for me. Yey!
Until the fix reaches the master branch you could maybe add the workaround to the docs, so people like me don't go crazy trying to figure out what's going wrong.
fricia is online now   Reply With Quote
Old 07-29-2022, 04:29 PM   #225
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by fricia View Post
So after working a couple days with it I'm slowly starting to get a grip on programming with rtk.
Great to hear. Always interested to hear feedback on things you found particularly confusing during the initial slope of the learning curve. I'm sure there's plenty of opportunity to improve the documentation or simplify the API.

Before getting into it, first thanks for the fully-baked copy/paste example. A few minor comments about your example code, for interest:
  • If you're not already coding to a particular style standard or aren't particularly opinionated about style, rtk's style guide might be worth following, at least to provide the best consistency with rtk itself.
  • No need for the onmouseenter/onmousemove handler for the rtk.Spacer widget. Just set the attribute cursor=rtk.mouse.cursors.SIZE_EW and rtk will do that for you.
  • This wasn't in your code, but I see you logging stuff in your screencap. You could have used rtk.log.info('changing to %s', currWidth) to print the message to REAPER's console. The logging API is much more ergonomic than reaper.ShowConsoleMsg().
  • w:reflow() shouldn't be necessary here. Although, because you're assigning e1.calc.w to currWidth but not doing anything with it, I'm guessing this was maybe leftover debugging code? Something you could have done instead is rtk.defer(rtk.log.info, 'e1 is %s', e1), which will log the widget on the next update cycle, after reflow has already happened. This logs a few things about the widget, including its geometry.
  • In ondragstart() it'd be better just to return true to indicate draggability rather than return event.x. Returning a fairly deliberate value like event.x implies you're doing something with it. In your example, you just want to return a truthy value to ensure ondragmousemove gets called.
  • When you have an rtk.Event available (as in your ondragmousemove handler), it's more idiomatic to use event.x instead of rtk.mouse.x. (They're equivalent, to be sure, but it's preferable to avoid accessing global state when you have the option.)
  • It's better not to specify an explicit 'h' attribute, as things can become a bit misaligned. In your example, you want the rtk.Spacer to be the same height as the rtk.Entry widgets in the box: the way to do this is to pass the stretch='siblings' and fillh=true cell attributes when you add the spacer to the hbox. hbox:add(rtk.Spacer{w=10, bg='dodgerblue'}, {stretch='siblings', fillh=true}). The stretch='siblings' cell attribute causes the cell to be the same height as the siblings (entry), and then fillh=true causes the rtk.Spacer to fill within that stretched cell.
  • You could also set minw on the rtk.Entry to ensure it can't shrink down so small it's unusable.


Quote:
Originally Posted by fricia View Post
The width the script is calculating and the width shown on the screen are not matching. The widget jumps everytime I start to drag and when dragging far distances cursor and widget end start to drift apart.
Yep, what's going on here is UI scaling. Notice that the size you're printing to console (which rtk calls the attribute's exterior value -- the value you pass in) and the size shown in the debug overlay in the UI (which is the computed value) is exactly 1.25x different. From the screenshot it looks like you're using Windows, so I'm guessing you're using 125% DPI scaling in Windows, or you have REAPER's own UI scaling enabled in the Advanced UI/System Tweaks section.

The relevant detail here is that, by default, widgets' scalability is "full" which means that pixel values for geometry are multiplied by the global scale.

The best solution is to divide by rtk.scale.value before assigning the width, because then it just gets adjusted back up when the width is calculated. A less good solution, but mentioning here for completeness, is to set the rtk.Entry's scalability attribute to 'box' which causes fixed geometry not to be adjusted by the global scale value.

So, taking everything mentioned above into account, the amended example looks like:

Code:
local w = rtk.Window{title='TESTING', w=600}
local hbox = w:add(rtk.HBox{padding='15 0'})
local e1 = hbox:add(rtk.Entry{w=110, minw=75, placeholder='entry'})
local resize = hbox:add(rtk.Spacer{w=10, bg='dodgerblue', cursor=rtk.mouse.cursors.SIZE_EW}, {stretch='siblings', fillh=true})
resize.ondragstart = function(self, event)
    -- Return true to indicate we support draggability
    return true
end
resize.ondragmousemove = function(self, event, dragarg)
    if not event.simulated then
        e1:attr('w', event.x / rtk.scale.value)
    end
end
w:open()


Quote:
Originally Posted by fricia View Post
2. In practice I would also like to use expand on the entries, so that they grow/shring when resizing the window. [...] However, as you can see, as soon I set the width of e1 by draging, fillw doesn't work anymore. Can this be circumvented somehow?
fillw's behavior is described as "If true, the child widget is asked to fill its width to the right edge of the container (unless the child itself has an explicitly defined width in which case this cell attribute is ignored)."

The parenthetical is what you're running into here. Once you set the 'w' attribute on the widget, you're defining an explicit width, meaning the widget will no longer fill the cell. The cell remains the same size, because of expand, but the rtk.Entry widget itself won't fill within it, instead it will respect the explicit width which takes precedence.

What you're trying to do -- essentially make a resizable pane -- isn't something I considered with the design. I understand the use case of course, and think this is something that ought to be better supported out-of-the-box. In the meantime, we should be able to find some way to beat things into submission, although it may be a bit hackish.

The solution that comes to my mind for this particular example is to skip the fillw=true (which implies expand=1, incidentally) on the first rtk.Entry and instead set a fixed width of 0.5 (50% of the parent container). But we still use fillw=true on the second rtk.Entry, to ensure it takes up all the remaining space.

Code:
local e1 = hbox:add(rtk.Entry{w=0.5, placeholder='entry1', minw=75})
local resize = hbox:add(rtk.Spacer{w=10, bg='dodgerblue', cursor=rtk.mouse.cursors.SIZE_EW}, {stretch='siblings', fillh=true})
local e2 = hbox:add(rtk.Entry{placeholder='entry2'}, {fillw=true})
This makes e1 50% of the hbox's parent's width (in this case that's the rtk.Window), and e2 fills (and expands, because fill in the box's direction implies expand) to what's left, minus the 10px needed for the rtk.Spacer.

Then you can continue to set the 'w' attribute on e1 in your ondragmousemove handler. It won't size lower than minw (75px). However it would be possible to size it larger to shrink e2 into oblivion -- in this case, setting minw on e2 won't help because the fixed width on e1 takes precedence. You'd have to do this calculation in ondragmousemove().

So that, I think, would basically work. But I imagine it'd break down fast if you tried more complex layouts.

Quote:
Originally Posted by fricia View Post
I played around with modifying the expand value instead of the width, but this was quite a bad experience and (correct me if I'm wrong) it really doesn't seem like the inteded way.
Definitely not. The expand attribute isn't in pixels, but "expand units" which is really just an arbitrary value that defines the amount the cell expands relative to all other expanded siblings. The percentage is expand / sum(expand of all children in container).

I don't think there's a particularly clean way to do what you're after yet. I'll have a think on it.


Quote:
Originally Posted by fricia View Post
And on a related note: Would there even be a quick way to get the expand behaviour (widget size follows window resizing) with a normal container?
By "normal container" do you mean rtk.Container? The expand cell attribute is specific to rtk.Box and subclasses, but rtk.Container does respect fillw/fillh. You could also specify fractional w/h on the widgets within the container to create a widget size that adapts to its parent size, as I showed in the above workaround.

Quote:
Originally Posted by fricia View Post
Until the fix reaches the master branch you could maybe add the workaround to the docs, so people like me don't go crazy trying to figure out what's going wrong.
Better still, I could get off my procrastinating ass and make a new release that has this fix.

I'll plan for that this weekend, unless I run into any major reasons to believe master isn't ready for release (in which case I'll update the docs instead as you suggested).

Last edited by tack; 07-29-2022 at 04:37 PM.
tack is online now   Reply With Quote
Old 07-30-2022, 03:06 AM   #226
fricia
Human being with feelings
 
Join Date: Apr 2017
Posts: 119
Default

WOW, Tack thank you so much for your detailed reply. I appreciate your effort!

Quote:
Originally Posted by tack View Post
Great to hear. Always interested to hear feedback on things you found particularly confusing during the initial slope of the learning curve. I'm sure there's plenty of opportunity to improve the documentation or simplify the API.
I will send you an email with some thoughts when I get around to it. Currently I'm just trying to brute force things until I understand them and it's working reasonably well which definitely speaks for the quality of your documentation. I'm certainly still at the beginning but I'm having a lot of fun here


Quote:
Originally Posted by tack View Post
Before getting into it, first thanks for the fully-baked copy/paste example. A few minor comments about your example code, for interest:
...

This is awesome feedback! The example code was just a script for testing so as you noted yourself there some leftovers from previous tries. Some things from your list I already knew, but still a lot of good stuff. Next time I will clean up my example code more to make my intent clearer and spare people reading my code some time.
I will definitely take a closer look at the logging API.


Quote:
Originally Posted by tack View Post
  • In ondragstart() it'd be better just to return true to indicate draggability rather than return event.x. Returning a fairly deliberate value like event.x implies you're doing something with it. In your example, you just want to return a truthy value to ensure ondragmousemove gets called.
That was one of the leftovers from previous tries, where I used the starting mouse position (event.x) to calculate stuff in the onmousemove handler. This would be the correct way to pass that value as the dragarg to the onmousemove function right? Are you just calling ondragstart() as an argument to onmousemove() behind the scenes? I was wondering how the passing on of parameters works there.


Quote:
Originally Posted by tack View Post
Yep, what's going on here is UI scaling. Notice that the size you're printing to console (which rtk calls the attribute's exterior value -- the value you pass in) and the size shown in the debug overlay in the UI (which is the computed value) is exactly 1.25x different. From the screenshot it looks like you're using Windows, so I'm guessing you're using 125% DPI scaling in Windows, or you have REAPER's own UI scaling enabled in the Advanced UI/System Tweaks section.
Ahhh that makes sense! I was just assuming rtk handles all the scaling for me everywhere. I will have a closer look at the scaling documentation and see if I can figure out where and how scaling is happening exactly.


Quote:
Originally Posted by tack View Post
The parenthetical is what you're running into here. Once you set the 'w' attribute on the widget, you're defining an explicit width, meaning the widget will no longer fill the cell. The cell remains the same size, because of expand, but the rtk.Entry widget itself won't fill within it, instead it will respect the explicit width which takes precedence.
While I was aware of that I was also hoping there would be secret way to override that behaviour


Quote:
Originally Posted by tack View Post
By "normal container" do you mean rtk.Container? The expand cell attribute is specific to rtk.Box and subclasses, but rtk.Container does respect fillw/fillh. You could also specify fractional w/h on the widgets within the container to create a widget size that adapts to its parent size, as I showed in the above workaround.

Yes that's what I meant. I think the way I'm gonna try to implement it is with HBox containers and relative widths. Will post again if I get any results (will be while though as I'm gonna be on holidays).

Thanks again for your support tack!
fricia is online now   Reply With Quote
Old 08-01-2022, 08:28 AM   #227
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Hi there!

Myk from Let's Talk Reaper did a feature on Buss Driver, and noted that the logo image doesn't appear in his script window on Windows.

Here's the script code. Line 187 is the rtk.ImageBox definition. Any idea why it might not appear? Like you, I'm on Mac so I don't have an easy way to test. I'm concerned perhaps the file path breaks Windows's ridiculously short string length limit.
MonkeyBars is offline   Reply With Quote
Old 08-01-2022, 08:50 AM   #228
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by MonkeyBars View Post
Myk from Let's Talk Reaper did a feature on Buss Driver
Cool!

Quote:
Originally Posted by MonkeyBars View Post
And noted that the logo image doesn't appear in his script window on Windows.
Actually it looks to me that the problem is that bussdriver_logo_nobg.png isn't included in the ReaPack.

At least, I don't see it here:

https://github.com/MonkeyBars3k/ReaS...ster/index.xml
tack is online now   Reply With Quote
Old 08-01-2022, 08:53 AM   #229
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
Actually it looks to me that the problem is that bussdriver_logo_nobg.png isn't included in the ReaPack.
Facepalm! Thanks tack.
MonkeyBars is offline   Reply With Quote
Old 08-09-2022, 06:17 AM   #230
fricia
Human being with feelings
 
Join Date: Apr 2017
Posts: 119
Default

Heeellooo again

I've been at it for a couple more hours (okay more like two days) and this is what I've come up with for resizable entries that also horizontally scale with the window. You can have an arbitrary number of entry fields and set a minimum width for the entries that is respected when resizing.



Try this (requires JS API):

Code:
local r = reaper
package.path = r.GetResourcePath() .. '/Scripts/rtk/1/?.lua'
local rtk = require('rtk')
local log = rtk.log
log.level = log.DEBUG
reaper.ClearConsole()

---- USER DEFINABLE VARIABLES ----
local lr_margin = 30
local tb_margin = 20
local entry_count = 3
local resize_w = 6
local entry_min_w = 100
local entry_h = 28
----------------------------------


function main()

  local w = rtk.Window{title='resizable entries testing', h=120, resizable=true, borderless=true} 

  w:open()

  local w_min_w = (entry_count * entry_min_w + lr_margin*2 + resize_w * (entry_count-1)) * rtk.scale.value
  local w_min_h = (entry_h + tb_margin * 2) * rtk.scale.value
  w:attr('minw', math.floor(w_min_w + 1))
  w:attr('minh', math.floor(w_min_h + 1))
  w:resize(w_min_w*1.4, w_min_h)

  local box = w:add(rtk.Container{w=w.w/rtk.scale.value, margin={tb_margin, 0}})

  local entry_w = (box.w - lr_margin*2 - resize_w*(entry_count-1))/entry_count

  local entries = {}
  local resize ={}

  entries[1] = box:add(rtk.Entry{placeholder='test1', x=lr_margin, w=entry_w, h=entry_h, border_hover='red'})

  for i = 2, entry_count do
    local resize_x = entries[i-1].x + entries[i-1].w
    local entry_x = entries[i-1].x + entries[i-1].w + resize_w

    resize[i-1] = box:add(rtk.Spacer{x=resize_x, w=resize_w, h=entry_h, bg='dodgerblue', cursor=rtk.mouse.cursors.SIZE_EW})
    entries[i] = box:add(rtk.Entry{placeholder='test'..tostring(i) ,x=entry_x, w=entry_w, h=entry_h, border_hover='red'})
  end

  local resize_start_w_left = 0
  local resize_start_w_right = 0

  for i = 1, entry_count-1 do

    resize[i].ondragstart = function(self, event)
        resize_start_w_left = entries[i].w
        resize_start_w_right = entries[i+1].w
        return event.x
    end
    

    resize[i].ondragmousemove = function(self, event, dragarg)

      if not event.simulated then

        local change = (event.x - dragarg) / rtk.scale.value
        local is_positive = change > 0

        local next_anchor = 0
        if i == entry_count - 1 then
          next_anchor = w.w / rtk.scale.value - lr_margin
        else
          next_anchor = resize[i+1].x
        end

        local min = entries[i].x + entry_min_w
        local max = next_anchor - entry_min_w
        local reached_min = resize_start_w_left + change <= entry_min_w
        local reached_max = resize_start_w_right - change <= entry_min_w
      
        if reached_max and is_positive then
          resize[i]:attr('x', max - resize_w)
          entries[i]:attr('w', resize[i].x - entries[i].x)
          entries[i+1]:attr('x', max)      
          entries[i+1]:attr('w', entry_min_w)
        elseif reached_min and not is_positive then
          resize[i]:attr('x', min)
          entries[i]:attr('w', entry_min_w)
          entries[i+1]:attr('x', resize[i].x + resize_w)      
          entries[i+1]:attr('w', next_anchor - entries[i+1].x)
        else
          resize[i]:attr('x', event.x / rtk.scale.value)
          entries[i]:attr('w', resize[i].x - entries[i].x)
          entries[i+1]:attr('x', resize[i].x + resize_w)      
          entries[i+1]:attr('w', next_anchor - entries[i+1].x)
        end
      end
    end
  end


  w.onresize = function(self, last_w)

    if entries[1].realized then
      local box_last_w = last_w / rtk.scale.value - lr_margin*2
      local w_change = (w.w - last_w) / rtk.scale.value

      box:attr('w', last_w / rtk.scale.value + w_change)
      
      for i = 1, entry_count do
        local entry_ratio = (entries[i].w + resize_w / entry_count) / box_last_w

        if i ~= 1 then
          entries[i]:attr('x', entries[i-1].x + entries[i-1].w + resize_w)
        end

        entries[i]:attr('w', entries[i].w + w_change * entry_ratio)
        
        if i ~= entry_count then
          resize[i]:attr('x', entries[i].x + entries[i].w)
        end
      end
    end
  end
end


main()

There are some minor imperfections here and there, but I think at least some of them are due to rounding errors, which I couldn't get rid of yet.

Now here are some questions:
  • Is there a specific reason why rtk.scale.value isn't calculated before the window is opened? I guess it doesn't matter too much, but all the action now happens after the window is opened, so I can calculate things properly.
  • Surpressing the event handling trigger when resizing the window with window:attr('w', w, false) didn't work. Is it supposed to?
  • Sadly using the margins attribute didn't quite work as I hoped. Somehow the entry to the righ always extended to and sometime beyond the width of the window. What did I miss there?
  • Is this code well optimized? I feel like I missed some things that could make it run smoother...
fricia is online now   Reply With Quote
Old 08-09-2022, 06:20 AM   #231
fricia
Human being with feelings
 
Join Date: Apr 2017
Posts: 119
Default

Quote:
Originally Posted by tack View Post
That'd be the way, onkeypress and check for event.keycode == rtk.keycodes.TAB, but you know I've got a good chunk of this logic already implemented in my local dev tree, for generic tab/shift-tab supported throughout the whole window. I've been sitting on this code for far too long.

If you're ok to wait until the weekend, I can get this committed so you can at least test it out and provide feedback?
And on another note: Did you ever get around to implementing this Tack?
fricia is online now   Reply With Quote
Old 08-11-2022, 03:17 PM   #232
jamesd256
Human being with feelings
 
jamesd256's Avatar
 
Join Date: Dec 2015
Location: Folkestone
Posts: 196
Default This looks like the way to go for me

Heya Tack,

Just a quick thanks and well done for rtk.

I like your attitude and values on software engineering. I've found myself marvelling at how you've approached crafting what is essentially an GUI toolkit in an app that's really an audio OS.

I like also that rtk is inspired by some of the paradigms of web application development (F12, poor man's devtools, love it. Debugging in Reaper, love it). And those docs tho. Great job!

Hell it seems like some things are better in rtk world. lpadding instead of padding-left. I get the thought process and this stuff matters to me. Sympathy for the coder is appreciated

Yeah I also thought we would have seen more exploitation of this lib, but at least you've got Monkeybars.

Anyhoo, I think I'll go down this route for my take on something like Heda's track templates, but woven into a looper paradigm based on core actions (thank you Kenny), and who knows, maybe some of Juliansander's stuff to try to do a helgoboss/Drivenbymoss/nabla approach to arranging.

Ultimately I want a performance oriented looper style / easy access to thousands of sounds type approach to music production. No, I'm not interested in just using Ableton - in too deep with Reaper

I asked you before about dynamic routing through Reaticulate to different instruments, but now I understand rtk, I'm keen to get going doing something using the underlying toolset. Thank you for all your dedicated hard work, your labour of love is not lost on me.

I may need to go beyond rtk to cover everything, but can't think of a better way to do what rtk does. Bravo!
jamesd256 is offline   Reply With Quote
Old 08-12-2022, 08:40 AM   #233
MonkeyBars
Human being with feelings
 
MonkeyBars's Avatar
 
Join Date: Feb 2016
Location: Hollyweird
Posts: 2,630
Default

Quote:
Originally Posted by jamesd256 View Post
I also thought we would have seen more exploitation of this lib, but at least you've got Monkeybars.
Holding down the fort!

RTK is killer, great job tack
MonkeyBars is offline   Reply With Quote
Old 08-14-2022, 04:25 PM   #234
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by fricia View Post
And on another note: Did you ever get around to implementing this Tack?
My good intentions are exceeded only my inability to deliver on them.

Unfortunately no. Well, I did work a bit further on it, but wasn't able to get it to a deliverable point before life pulled me in the opposite direction. Unless something surprises me in the next couple days, I do hope to have some proper hacking time this week. Stay tuned.

Quote:
Originally Posted by fricia View Post
I've been at it for a couple more hours (okay more like two days) and this is what I've come up with for resizable entries that also horizontally scale with the window. You can have an arbitrary number of entry fields and set a minimum width for the entries that is respected when resizing.
This is clever (and looks cool). A great example of how sheer force of will can overcome design limitations.

So now that you've done this, if this basic capability were provided natively, what do you think the API should look like and how do you think it should work? (I have my own ideas but don't want to poison the well.)


Quote:
Originally Posted by fricia View Post
Is there a specific reason why rtk.scale.value isn't calculated before the window is opened? I guess it doesn't matter too much, but all the action now happens after the window is opened, so I can calculate things properly.
The latest stable release doesn't, but the test build from master does. There was a reason the calculation didn't happen until the window was opened, but I managed to overcome that.

I'm slightly shocked to realize that the commit that adds this was back in March. I quite urgently need to push a new release. Maybe that's what I'll spend this week's hacking time on (sorry ).


Quote:
Originally Posted by fricia View Post
Surpressing the event handling trigger when resizing the window with window:attr('w', w, false) didn't work. Is it supposed to?
Kinda. The devil's in the details.

The docs say "if false, event handlers that would normally fire will be suppressed even if the value changed (except for onattr which is always fired if the value changes)."

IIRC, the use case that caused me to add this argument was setting the initial value of an rtk.OptionMenu and wanting to avoid invoking my own onchange() handler. So it's not that the normal behavior which occurs when you set the attribute is completely bypassed, only that user-attached event handlers won't be invoked.

In your case, the internal logic in rtk.Window used to handle geometry changes is implemented via onattr() (or rather rtk.Window's analogous internal method called _handle_attr()), which per above does still get invoked. I'd be interested to learn more about the use case of why you wanted to set the 'w' attribute on rtk.Window but bypass the existing resize logic.

The master branch (and this will probably land in the next release) does have a new method to do just this: rtk.Widget:sync(). But the use case here is probably quite different than yours: it's a mechanism to synchronize non-user-driven or external (outside rtk) changes back to the attribute. For example, if rtk notices the window is resized, the attribute needs to be updated to reflect this change. In this example it's not like :attr() is being used to change the window geometry, only that rtk passively noticed this and needs to update its internal state to reflect the change.

And I'm using :sync() in the context of reactives, which is something discussed back on page 1 of this thread, and something for which I've had code baking a rather long time. I've been waffling about it, because reactives are seriously cool, but they're also extraordinarily magical in implementation and I worry about the inevitable edge cases and bugs that will be difficult to fix. (I will probably release them as experimental, to get feedback.)

Anyway, popping this unexpected tangent off the stack, your needs were probably quite different, so I'd be interested to understand what you had in mind so I can see if the new :sync() method would fit the bill. Or maybe there's another way.

Quote:
Originally Posted by fricia View Post
Sadly using the margins attribute didn't quite work as I hoped. Somehow the entry to the righ always extended to and sometime beyond the width of the window. What did I miss there?
I think I'd probably need a condensed example to see what you mean. This could be a bug. I've fixed some container layout bugs in this area too, so there is a remote possibility that the test build from master might work better.

Quote:
Originally Posted by fricia View Post
Is this code well optimized? I feel like I missed some things that could make it run smoother...
Not tested, but seeing as you're basically implementing a custom layout, something you could try is passing rtk.Widget.REFLOW_PARTIAL to attr()'s reflow argument. Full reflows are among the more expensive operations rtk needs to do, and any geometry change on any widget will trigger a full reflow. But you might just already be calculating everything that needs it in your event handlers, so it's worth seeing if you can get away with a partial reflow (which just does an internal reflow within the specific widget(s) that changed).

Otherwise it's probably as good as you can do given rtk's current constraints and the behavior you were going for.
tack is online now   Reply With Quote
Old 08-14-2022, 04:37 PM   #235
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by jamesd256 View Post
I like your attitude and values on software engineering. I've found myself marvelling at how you've approached crafting what is essentially an GUI toolkit in an app that's really an audio OS.
Thanks!

I have often considered that I have gone completely off the rails with rtk, over-engineering a niche library for a niche use case for a niche product. With some 25k lines of code and counting, I've spent far more time coding than making music, which makes me feel a little guilty, but is also probably a fair indictment of my talent in one area relative to the other.


Quote:
Originally Posted by jamesd256 View Post
I get the thought process and this stuff matters to me. Sympathy for the coder is appreciated
That's very gratifying to hear. Thanks.


Quote:
Originally Posted by jamesd256 View Post
No, I'm not interested in just using Ableton - in too deep with Reaper
I sure know how that is.

Of course, REAPER still has a very special way of pissing me off, and the frustration I experience when I consider any number of tiny improvements the REAPER devs could make to vastly improve quality of life for its users and script-writers, but then contemplate the sheer hopelessness of screaming into that empty black hole known as the Feature Requests subforum and it fills me with despair.

But then I occasionally flirt with other DAWs and quickly realize the grass is certainly not greener on the other side, it's just a rather different shade of brown, with a different set of barren patches.

So, I too am rather over-invested in REAPER at this point.


Quote:
Originally Posted by jamesd256 View Post
I asked you before about dynamic routing through Reaticulate to different instruments, but now I understand rtk, I'm keen to get going doing something using the underlying toolset. Thank you for all your dedicated hard work, your labour of love is not lost on me.
Awesome. I can't wait to see what you build!

Thanks for the very kind words.
tack is online now   Reply With Quote
Old 08-15-2022, 04:22 AM   #236
_Stevie_
Human being with feelings
 
_Stevie_'s Avatar
 
Join Date: Oct 2017
Location: Black Forest
Posts: 5,054
Default

Quote:
Originally Posted by tack View Post

Of course, REAPER still has a very special way of pissing me off, and the frustration I experience when I consider any number of tiny improvements the REAPER devs could make to vastly improve quality of life for its users and script-writers, but then contemplate the sheer hopelessness of screaming into that empty black hole known as the Feature Requests subforum and it fills me with despair.
Unfortunately very true, however, I can see some movement, lately, like Retrorec.
But many other long-standing requests that would be an easy fix and help us to reduce frustration are still ignored.
__________________
My Reascripts forum thread | My Reascripts on GitHub
If you like or use my scripts, please support the Ukraine: Ukraine Crisis Relief Fund | DirectRelief | Save The Children | Razom
_Stevie_ is offline   Reply With Quote
Old 08-18-2022, 07:01 AM   #237
fricia
Human being with feelings
 
Join Date: Apr 2017
Posts: 119
Default

Quote:
Originally Posted by tack View Post
So now that you've done this, if this basic capability were provided natively, what do you think the API should look like and how do you think it should work? (I have my own ideas but don't want to poison the well.)
First, entries should have a 'resizable' attribute. When set to true, hovering over the left or right edge of the entry changes the cursor image to 'resize'. Clicking and dragging then changes the 'x' and 'w' attributes of the entry as needed. Resizing in this way should respect min/max width. If you place an entry in a container like HBox, where widgets are automatically positioned, resizing an entry should automatically resize and reposition the previous/next element in the Box (also respecting their respect min/max widht). In this case the resized entry and it's neighbouring widget in the direction of the expansion/compression is affected.
Then you could add another attribute that when set to true, changes the behaviour to move all elements on the side of the entry that is being extended. In this case the x position of all widgets on the side that is expanded/compressed are affected, but not their width.
Until vertical scrolling is implemented things should generally only be expandable until something hits the edge of the container/window margin.
If placed in a normal container and set to resizable, some form of collision detection would need to be implemented, so you can't extend entries over other widgets (or maybe it's the job of the programmer to make sure this doesn't happen by defining max width?)


No idea if this is a smart way to implement it, just the first idea that came to my mind.


Quote:
Originally Posted by tack View Post
Anyway, popping this unexpected tangent off the stack, your needs were probably quite different, so I'd be interested to understand what you had in mind so I can see if the new :sync() method would fit the bill. Or maybe there's another way.
Two things:
1. In my script the number of entry fields should eventually be user configurable. Since I need to create the window before I can add the entry fields I wanted to use :attr to set the window size to something sensible in regards to the amount of entry fields.
2. I was also thinking it would be nice if dragging the left edge of the leftmost and the right edge of the rightmost entry would not only resize the entry, but also resize the window to fit the enlargened entry. However, because I'm using the window.onresize handler to scale all the entries in the window to the new window size this obviously doesn't work.

One solution would be to just use the JS API functions to the window resizing in that case.

EDIT: Apparently using JS_Window_Resize also calls the window.onresize handler for some reason...

Quote:
Originally Posted by tack View Post
Not tested, but seeing as you're basically implementing a custom layout, something you could try is passing rtk.Widget.REFLOW_PARTIAL to attr()'s reflow argument.
...
Thanks I will try this!


And I can only +1 all the people thanking you for your hard work and support! Just makes it that much more fun to work on things other than the ones you actually should (aka music).

Last edited by fricia; 08-20-2022 at 12:59 AM.
fricia is online now   Reply With Quote
Old 10-29-2022, 08:29 AM   #238
fricia
Human being with feelings
 
Join Date: Apr 2017
Posts: 119
Default

I finally got around to releasing my first project with RTK:

https://forum.cockos.com/showthread.php?t=272168

Thank you so much Tack for making this possible!
__________________
Metadata Manager
fricia is online now   Reply With Quote
Old 11-07-2022, 02:15 PM   #239
tompad
Human being with feelings
 
Join Date: Jan 2010
Location: Fjugesta, Sweden
Posts: 811
Default

Hi Tack and all!

I have a problem with text wrap, I have looked at this code and
tried different approaches but am still failing:

Code:
local t = {
    'Toggle show only selected tracks in mixer.', 
    'Create an rtk.Window object that is to be.', 
    'that is to be the main application.', 
    'Window object that is to be.', 
    'Window object that is to be.', 
    'that is to be the main application.', 
    'Window object that is to be.', 
    'Window object that is to be.'}


-- Add another box
local box2 = window:add(rtk.HBox{w=500, h=180, x= 22,tmargin=60, hspacing=145})
for key, val in pairs(t) do
    v = tostring(val)
     box2:add(rtk.Text{v, w=155, border='red', wrap=true,overflow=false, halign='center', h=200, rmargin=28, fontsize=22 })
   
end
This is what I get(see attachment) - why is some of the text not wraped, and why is it different
wrapping on some?
Attached Images
File Type: png Wrappeliwrap (Liten).PNG (19.5 KB, 50 views)
__________________
ToDoList Obliques MusicMath Donation Some of mine and my friends music projects on Spotify
tompad is offline   Reply With Quote
Old 11-07-2022, 05:40 PM   #240
tack
Human being with feelings
 
tack's Avatar
 
Join Date: Jan 2014
Location: Ontario, Canada
Posts: 1,618
Default

Quote:
Originally Posted by fricia View Post
I finally got around to releasing my first project with RTK:

https://forum.cockos.com/showthread.php?t=272168
Wonderful! Congrats on the release, fricia. Looks like a great contribution to the community.
tack is online now   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 02:19 PM.


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