Old 03-06-2021, 08:02 AM   #1
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default ReaImGui: ReaScript binding for Dear ImGui

REAPER Immediate-Mode Graphical User Interface

ReaImGui is a ReaScript binding and REAPER backend for the Dear ImGui toolkit. It adds over 380 API functions (more than 800 including constants!) for creating GPU-rendered GUI interfaces.

This REAPER extension plugin can be installed using ReaPack (through the default ReaTeam Extensions repository).




About Dear ImGui

Dear ImGui is an implementation of the immediate-mode GUI paradigm. Applications using this paradigm create their GUI on the fly without retaining and managing the UI toolkit's state themselves.

This is particularly well suited to ReaScripts by making use of the global 33Hz timer to describe each UI frames.

See Dear ImGui's FAQ.

Example code #1



This example shows the bare-minimum code needed to display a window using ReaImGui.

Code:
local ctx = reaper.ImGui_CreateContext('My script')

local function loop()
  local visible, open = reaper.ImGui_Begin(ctx, 'My window', true)
  if visible then
    reaper.ImGui_Text(ctx, 'Hello World!')
    reaper.ImGui_End(ctx)
  end
  if open then
    reaper.defer(loop)
  end
end

reaper.defer(loop)
Example code #2



This example loads a font and receives user input.

Code:
dofile(reaper.GetResourcePath() ..
       '/Scripts/ReaTeam Extensions/API/imgui.lua')('0.8')

local ctx = reaper.ImGui_CreateContext('My script')
local sans_serif = reaper.ImGui_CreateFont('sans-serif', 13)
reaper.ImGui_Attach(ctx, sans_serif)

click_count, text = 0, 'The quick brown fox jumps over the lazy dog'

local function myWindow()
  local rv

  if reaper.ImGui_Button(ctx, 'Click me!') then
    click_count = click_count + 1
  end
  if click_count % 2 == 1 then
    reaper.ImGui_SameLine(ctx)
    reaper.ImGui_Text(ctx, [[hello dear imgui! \o/]])
  end
  
  rv, text = reaper.ImGui_InputText(ctx, 'text field', text)
end

local function loop()
  reaper.ImGui_PushFont(ctx, sans_serif)
  reaper.ImGui_SetNextWindowSize(ctx, 400, 80, reaper.ImGui_Cond_FirstUseEver())
  local visible, open = reaper.ImGui_Begin(ctx, 'My window', true)
  if visible then
    myWindow()
    reaper.ImGui_End(ctx)
  end
  reaper.ImGui_PopFont(ctx)
  
  if open then
    reaper.defer(loop)
  end
end

reaper.defer(loop)
An EEL2 example is provided as ReaImGui_Hello World.eel along with the extension. Python and C++ examples are available on GitHub.

Example code #3

Dear ImGui comes with a comprehensive demo application showcasing most features. A partial ReaScript port of it is provided along with the extension as ReaImGui_Demo.lua.

Documentation

A custom HTML documentation is accessible via the Documentation button in the demo script (or by opening <resource path>/Data/reaper_imgui_doc.html).

It is also available online: reaper_imgui_doc.html.



Additional tools for Lua scripts

Backward-compatibility shims

Lua scripts may request a specific version of ReaImGui's API:

Code:
dofile(reaper.GetResourcePath() ..
       '/Scripts/ReaTeam Extensions/API/imgui.lua')
  ('X.Y.Z') -- current version at the time of writing the script
gfx to ReaImGui translation

Lua scripts can use a translation layer to re-implement most gfx functions using ReaImGui (discussion thread):

Code:
gfx = dofile(reaper.GetResourcePath() ..
             '/Scripts/ReaTeam Extensions/API/gfx2imgui.lua')
Minimum system requirements

REAPER v5.979 (v6.58 or newer recommended)
Linux, macOS 10.9 or Windows Vista
Required system libraries on Linux: GDK3 3.22, freetype, libepoxy, libpng, libstdc++ for GCC 5.1

Workarounds for REAPER API limitations
  • Writable arguments are listed before optional arguments in order to be returned first. Example: ImGui_GetMouseDragDelta [t=266396]
  • Extensions may only register functions, so constant values and flags are exposed as functions
  • Extension functions cannot have callback parameters. ReaImGui provides a way to create EEL function objects to work around this.
Contributing

The source code is available under LGPLv3 on GitHub. Patches (including documentation edits) can be submitted as pull requests. Send bug reports on the issue tracker.

Last edited by cfillion; 01-20-2024 at 07:56 PM. Reason: ReaImGui != RealmGui
cfillion is offline   Reply With Quote
Old 03-06-2021, 08:24 AM   #2
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

HUGE MOMENT
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 03-06-2021, 09:07 AM   #3
Vagelis
Human being with feelings
 
Vagelis's Avatar
 
Join Date: Oct 2017
Location: Larisa, Greece
Posts: 3,799
Default

Awesome! I just wish to see in future scripts a universal size of the GUI for the docked scripts. This would be very useful and way more clean to work with.
An example is Ableton or Bitwig devices, where they have the same height in the docker and you can navigate and choose easier the parameters.
Vagelis is offline   Reply With Quote
Old 03-06-2021, 10:07 AM   #4
_Stevie_
Human being with feelings
 
_Stevie_'s Avatar
 
Join Date: Oct 2017
Location: Black Forest
Posts: 5,054
Default

Quote:
Originally Posted by gxray View Post
HUGE MOMENT
YES, this is HUGE. Thanks cfillion, you rock!
__________________
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 03-06-2021, 12:22 PM   #5
IXix
Human being with feelings
 
Join Date: Jan 2007
Location: mcr:uk
Posts: 3,889
Default

Awww man, not another thing to look into! LOL!
IXix is offline   Reply With Quote
Old 03-06-2021, 12:55 PM   #6
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

For those who might not be familiar, brief examples of the kinds of UI's which have been built using ImGUI:
__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 03-06-2021, 01:15 PM   #7
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 1,992
Default

woooooow this exemples are neat!

Thanks a lot for bringing this to reaper !
:O
Can't wait to see more GUI Exemples using this, will try probably this week to do one!
daniellumertz is online now   Reply With Quote
Old 03-06-2021, 02:17 PM   #8
Infrabass
Human being with feelings
 
Join Date: Apr 2014
Posts: 398
Default

If I understand well, this is really, REALLY cool!

Your contributions to the community are so impactful!

THANK YOU!
Infrabass is offline   Reply With Quote
Old 03-06-2021, 07:26 PM   #9
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 1,992
Default

cfillion do you know if there is a place I can look/search the functions/methods?
I am looking at their git but a little confused as the info is sparsed in pages, wondering if there is a list that I just didn't found.

Also I can import images with your version?

Looking at the code in the big example nice work!

Thanks a lot again!
daniellumertz is online now   Reply With Quote
Old 03-06-2021, 07:43 PM   #10
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

REAPER's built-in ReaScript documentation (Help menu) is the best way to find all functions (and descriptions). All functions start with "ImGui_".

No images (or custom font) support at this time, but it's something I'd like to add.

Last edited by cfillion; 03-06-2021 at 08:01 PM.
cfillion is offline   Reply With Quote
Old 03-06-2021, 08:01 PM   #11
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 1,992
Default

ohh, I didn't know they would start to appear there that is coooolll! Thanks, will continue here! Images and fonts will would be a great add!
daniellumertz is online now   Reply With Quote
Old 03-06-2021, 08:30 PM   #12
dsyrock
Human being with feelings
 
dsyrock's Avatar
 
Join Date: Sep 2018
Location: China
Posts: 565
Default

Is there any document about how to use it?

EDIT:Found it! Thanks!

Last edited by dsyrock; 03-06-2021 at 09:39 PM.
dsyrock is offline   Reply With Quote
Old 03-06-2021, 10:30 PM   #13
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 1,992
Default

Hey this is amazing !
I really would like to see more examples as people do, I will share mine that did for study the lib.
Also having a hard time creating a menu if someone have a simple example...





Code:
r = reaper
local click_count, text = 0, ''
local ctx = reaper.ImGui_CreateContext('Not Midi Transfer', 300, 325)
layer = 1

function print(a)
    reaper.ShowConsoleMsg('\n'..tostring(a))
end

function HelpMarker(desc) -- Function to show a ?
    reaper.ImGui_TextDisabled(ctx, '(?)')
    if reaper.ImGui_IsItemHovered(ctx) then
      reaper.ImGui_BeginTooltip(ctx)
      reaper.ImGui_PushTextWrapPos(ctx, reaper.ImGui_GetFontSize(ctx) * 35.0)
      reaper.ImGui_Text(ctx, desc)
      reaper.ImGui_PopTextWrapPos(ctx)
      reaper.ImGui_EndTooltip(ctx)
    end
end

function ToolTip(text)
    if reaper.ImGui_IsItemHovered(ctx) then
        reaper.ImGui_BeginTooltip(ctx)
        reaper.ImGui_PushTextWrapPos(ctx, reaper.ImGui_GetFontSize(ctx) * 35.0)
        reaper.ImGui_Text(ctx, text)
        reaper.ImGui_PopTextWrapPos(ctx)
        reaper.ImGui_EndTooltip(ctx)
    end
end

function loop()
    local rv

    if reaper.ImGui_IsCloseRequested(ctx) then
        reaper.ImGui_DestroyContext(ctx)
        return
    end
    
    local window_flags = reaper.ImGui_WindowFlags_MenuBar() |
                     reaper.ImGui_WindowFlags_NoDecoration()
    reaper.ImGui_SetNextWindowPos(ctx, 0, 0)
    reaper.ImGui_SetNextWindowSize(ctx, reaper.ImGui_GetDisplaySize(ctx))
    reaper.ImGui_Begin(ctx, 'Window', nil, window_flags)


    ----------------------------------------
    ---------------------------------------²
    -----------------------------------------
    ---- GUI

    if reaper.ImGui_BeginMenuBar(ctx) then
        if reaper.ImGui_BeginMenu(ctx, 'File') then
            if reaper.ImGui_MenuItem(ctx, 'Open') then
                reaper.ShowConsoleMsg('opening...\n')
            end
            if reaper.ImGui_MenuItem(ctx, 'Save') then
                reaper.ShowConsoleMsg('saving...\n')
            end
            reaper.ImGui_EndMenu(ctx)
        end
        reaper.ImGui_EndMenuBar(ctx)
    end
    
    if reaper.ImGui_Button(ctx, 'Click me!') then
        click_count = click_count + 1
        reaper.ShowConsoleMsg("aaa")
    end
    
    reaper.ImGui_SameLine(ctx)
    reaper.ImGui_Text(ctx, [[\o/]])
    

    reaper.ImGui_SameLine(ctx)
    HelpMarker('Need a help friend?\nI once took an arrow in the kneww')

    ------------
    rv, text = reaper.ImGui_InputText(ctx, 'text input', 'thanks cfillion  '..tostring(click_count))
    ------------- Slider
    if not v then v = 5 end
    retval,  v = reaper.ImGui_SliderDouble(ctx,  'Log', v, 10, 30, v,  reaper.ImGui_SliderFlags_Logarithmic()) -- Window, Name, Value, Min, Max, opt string in the middle, flags for log or stuff 

    -------------- Checkbox
    if not type(bol) then bol = true end
    rc, bol = r.ImGui_Checkbox(ctx, 'What do you want from me', bol)
    -------------- Radio
    if not val then val = 1 end
    rv, val = r.ImGui_RadioButtonEx(ctx, 'radio a', val, 0); r.ImGui_SameLine(ctx)
    rv, val = r.ImGui_RadioButtonEx(ctx, 'radio b', val, 1); r.ImGui_SameLine(ctx)
    rv, val  = r.ImGui_RadioButtonEx(ctx, 'radio c', val, 2)
    -------------- Many Butto funny
    for i = 0, 4 do
        if i > 0 then
         r.ImGui_SameLine(ctx)
        end
        r.ImGui_PushID(ctx, i)
        local buttonColor  = reaper.ImGui_ColorConvertHSVtoRGB(i / 7.0, 0.6, 0.6, 1.0)
        local hoveredColor = reaper.ImGui_ColorConvertHSVtoRGB(i / 7.0, 0.7, 0.7, 1.0)
        local activeColor  = reaper.ImGui_ColorConvertHSVtoRGB(i / 7.0, 0.8, 0.8, 1.0)
        r.ImGui_PushStyleColor(ctx, r.ImGui_Col_Button(),        buttonColor)
        r.ImGui_PushStyleColor(ctx, r.ImGui_Col_ButtonHovered(), hoveredColor)
        r.ImGui_PushStyleColor(ctx, r.ImGui_Col_ButtonActive(),  activeColor)
        r.ImGui_Button(ctx, 'Click')
        r.ImGui_PopStyleColor(ctx, 3)
        r.ImGui_PopID(ctx)
        ToolTip('Can you eat buttons?')
        if reaper.ImGui_IsItemClicked( ctx) then
            print("You Can! But Just "..i+1)
        end
    end

    ------------------------------------ Above again
        r.ImGui_PushID(ctx, 0)
        if not vari then vari = 0.01 end
        if not hues then hues = 0 end
        hues = hues + vari 
        local buttonColor  = reaper.ImGui_ColorConvertHSVtoRGB( 0.1+hues, 0.6, 0.6, 1.0)
        local hoveredColor = reaper.ImGui_ColorConvertHSVtoRGB( 7.0, 0.7, 0.7, 1.0)
        local activeColor  = reaper.ImGui_ColorConvertHSVtoRGB( 7.0, 0.8, 0.8, 1.0)
        r.ImGui_PushStyleColor(ctx, r.ImGui_Col_Button(),        buttonColor)
        r.ImGui_PushStyleColor(ctx, r.ImGui_Col_ButtonHovered(), hoveredColor)
        r.ImGui_PushStyleColor(ctx, r.ImGui_Col_ButtonActive(),  activeColor)
        r.ImGui_Button(ctx, 'Danger', 250, 100)
        retval,  vari  = reaper.ImGui_SliderDouble(ctx,  'H', vari, 0, 0.2) -- Window, Name, Value, Min, Max, opt string in the middle, flags for log or stuff 
        r.ImGui_PopStyleColor(ctx, 3)
        r.ImGui_PopID(ctx)

    --------------------------------------
    ---------------------------------------
    --------------------------------------
    reaper.ImGui_End(ctx)
    reaper.defer(loop)
end

loop()

Here another one that might help someone It is the simplest I could get, like a blank GUI, with nothing in it just open the screen (there is two options lines).

Code:
function GuiInit()
    ctx = reaper.ImGui_CreateContext('Item Sequencer') -- Add VERSION TODO
    FONT = reaper.ImGui_CreateFont('sans-serif', 15) -- Create the fonts you need
    reaper.ImGui_AttachFont(ctx, FONT)-- Attach the fonts you need
end

function loop()

    local window_flags = reaper.ImGui_WindowFlags_MenuBar() 
    reaper.ImGui_SetNextWindowSize(ctx, 250, 300, reaper.ImGui_Cond_Once())-- Set the size of the windows.  Use in the 4th argument reaper.ImGui_Cond_FirstUseEver() to just apply at the first user run, so ImGUI remembers user resize s2
    reaper.ImGui_PushFont(ctx, FONT) -- Says you want to start using a specific font

    local visible, open  = reaper.ImGui_Begin(ctx, 'Robert Green Blue ', true, window_flags)

    if visible then
        --------
        --YOUR GUI HERE
        --------
        reaper.ImGui_End(ctx)
    end 


    reaper.ImGui_PopFont(ctx) -- Pop Font

    if open then
        reaper.defer(loop)
    else
        reaper.ImGui_DestroyContext(ctx)
    end
end

GuiInit()
loop()

Last edited by daniellumertz; 08-26-2021 at 10:08 PM.
daniellumertz is online now   Reply With Quote
Old 03-06-2021, 10:39 PM   #14
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Quote:
Originally Posted by daniellumertz View Post
Also having a hard time creating a menu if someone have a simple example...
Code:
local window_flags = reaper.ImGui_WindowFlags_MenuBar() |
                     reaper.ImGui_WindowFlags_NoDecoration()
reaper.ImGui_SetNextWindowPos(ctx, 0, 0)
reaper.ImGui_SetNextWindowSize(ctx, reaper.ImGui_GetDisplaySize(ctx))
reaper.ImGui_Begin(ctx, 'Window', nil, window_flags)

if reaper.ImGui_BeginMenuBar(ctx) then
  if reaper.ImGui_BeginMenu(ctx, 'File') then
    if reaper.ImGui_MenuItem(ctx, 'Open') then
      reaper.ShowConsoleMsg('opening...\n')
    end
    if reaper.ImGui_MenuItem(ctx, 'Save') then
      reaper.ShowConsoleMsg('saving...\n')
    end
    reaper.ImGui_EndMenu(ctx)
  end
  reaper.ImGui_EndMenuBar(ctx)
end

reaper.ImGui_End(ctx)
cfillion is offline   Reply With Quote
Old 03-06-2021, 11:32 PM   #15
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 1,992
Default

Thanks Cfillion!
I was forgetting the flags! I update my example with yours here on the forum! Looking neat!

Other question, if you don't mind, how would you make layers? EDIT? I said layers but meant tabs, like changing the objects you see

I was thinking in putting the objects between if conditions but if it don't happen it might change objects place around, so it might need to be counted but not draw, or with 0 alpha. Thanks for the responses!

Last edited by daniellumertz; 03-07-2021 at 01:12 PM.
daniellumertz is online now   Reply With Quote
Old 03-08-2021, 02:31 AM   #16
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Quote:
Originally Posted by daniellumertz View Post
Other question, if you don't mind, how would you make layers? EDIT? I said layers but meant tabs, like changing the objects you see

I was thinking in putting the objects between if conditions but if it don't happen it might change objects place around, so it might need to be counted but not draw, or with 0 alpha. Thanks for the responses!
I'm not sure I understand what you mean exactly. If you mean actual tabs, ImGui has those, see Widgets > Tabs in the demo script. If you mean to have some kind of "placeholder widget" that takes room in the layout but is not drawn, this can be done with ImGui_Dummy (or ImGui_InvisibleButton).
cfillion is offline   Reply With Quote
Old 03-08-2021, 03:29 AM   #17
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Released version 0.1.1:

Code:
• Add EEL2 example using the legacy < 6.24 syntax [#1]
• Fix first frame having twice the contents in the example scripts
• macOS: process the first click over an unfocused window
• Remove focus from ImGui input controls when the context window itself loses focus

Docking bugfixes:
• Fix keyboard input going to REAPER when docked
• Skip rendering when docked and another docker tab is active
• Linux & Windows: fix mousewheel events leaking to REAPER
• Linux: fix crashes, flickering and incomplete rendering
• macOS: update viewport size if it changed while another docker tab was active
• Windows: don't lose focus when Tab is pressed

Last edited by cfillion; 03-08-2021 at 05:54 AM. Reason: s/5.24/6.24
cfillion is offline   Reply With Quote
Old 03-08-2021, 01:30 PM   #18
Indiscipline
Human being with feelings
 
Indiscipline's Avatar
 
Join Date: Apr 2016
Posts: 143
Default

This was bound to happen, but huge thanks to cfillion for making the inevitable happen sooner rather than later.

How long till someone codes a wasm interpreter so we could finally run Windows inside Reaper?
Indiscipline is offline   Reply With Quote
Old 03-08-2021, 04:48 PM   #19
Arthur McArthur
Human being with feelings
 
Arthur McArthur's Avatar
 
Join Date: Sep 2016
Location: Toronto
Posts: 744
Default

Thank you so much for this cfillon, what an incredible toolkit! It's inspired me to finally learn scripting and I've started on something I've wanted for a long time: an item arpeggiator script. Here's a preview: https://youtu.be/BtE5hqbbdos . I need to figure out a lot of things before it's ready like presets and smarter controls.

Somethings I'm still struggling to understand, could someone post an example of a vertical slider that has these attributes?

-it starts up with a definable default value
-double click on the control returns it to the default value
-after clicking it and releasing the mouse, it runs a function immediately
-it responds to mousewheel (and shift-mousewheel for finer adjustement if possible)
Arthur McArthur is offline   Reply With Quote
Old 03-09-2021, 12:24 AM   #20
Infrabass
Human being with feelings
 
Join Date: Apr 2014
Posts: 398
Default

Nice to see the first real applications! Good job Arthur!
Infrabass is offline   Reply With Quote
Old 03-09-2021, 01:37 AM   #21
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 1,992
Default

Quote:
Originally Posted by cfillion View Post
I'm not sure I understand what you mean exactly. If you mean actual tabs, ImGui has those, see Widgets > Tabs in the demo script. If you mean to have some kind of "placeholder widget" that takes room in the layout but is not drawn, this can be done with ImGui_Dummy (or ImGui_InvisibleButton).
Thanks a lot for the info! Will give a look at this week.

One thing. If my script calls a message box (that needs input) the ImGui breaks I think... You might already know just wanna report, just in case...
daniellumertz is online now   Reply With Quote
Old 03-09-2021, 05:49 AM   #22
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Quote:
Originally Posted by Arthur McArthur View Post
could someone post an example of a vertical slider that has these attributes?

-it starts up with a definable default value
-double click on the control returns it to the default value
-after clicking it and releasing the mouse, it runs a function immediately
-it responds to mousewheel (and shift-mousewheel for finer adjustement if possible)
Code:
local default_value = 42
local value = default_value
local reset = {}

function resetOnDoubleClick(id, value, default)
  if reaper.ImGui_IsItemDeactivated(ctx) and reset[id] then
    reset[id] = nil
    return default
  elseif reaper.ImGui_IsItemHovered(ctx) and reaper.ImGui_IsMouseDoubleClicked(ctx, 0) then
    reset[id] = true
  end
  
  return value
end

function applyMousewheel(value, min, max)
  local shift = reaper.ImGui_GetKeyMods(ctx) & reaper.ImGui_KeyModFlags_Shift() ~= 0
  local wheel, hwheel = reaper.ImGui_GetMouseWheel(ctx)
  if shift and wheel == 0 then wheel = hwheel end

  if wheel ~= 0 then
    speed = shift and 1 or 4
    value = value + math.ceil(wheel * speed)
    return math.max(min, math.min(value, max))
  end
  
  return value
end

function myVSliderInt(id, width, height, value, min, max, default_value)
  local rv
  rv,value = reaper.ImGui_VSliderInt(ctx, id, width, height, value, min, max)
  value = resetOnDoubleClick(id, value, default_value)

  if reaper.ImGui_IsItemHovered(ctx) then
    value = applyMousewheel(value, min, max)
  end

  -- or ImGui_IsItemDeactivatedAfterEdit to only do something if the value changed
  return reaper.ImGui_IsItemDeactivated(ctx), value
end

function loop()
  local rv

  -- ...

  rv,value = myVSliderInt('##v', 40, 160, value, 0, 100, default_value)
  if rv then
    doSomething()
  end

  -- ...
end
Quote:
Originally Posted by daniellumertz View Post
One thing. If my script calls a message box (that needs input) the ImGui breaks I think... You might already know just wanna report, just in case...
I wasn't aware of that issue, thanks! Seems like calling ShowMessageBox/GetUserInputs on Windows and Linux pauses all scripts but not the extension, which proceeds to delete the contextes thinking the are no longer in use. I'll look into possible solutions...

An alternative would be to use ImGui's own modal dialog feature. See Demo > Popups & Modal windows > Modals.

Last edited by cfillion; 03-09-2021 at 06:48 AM.
cfillion is offline   Reply With Quote
Old 03-09-2021, 10:17 AM   #23
Arthur McArthur
Human being with feelings
 
Arthur McArthur's Avatar
 
Join Date: Sep 2016
Location: Toronto
Posts: 744
Default

Thanks so much cfillon!

Here's a bit of code testing it out, it seems like the mousewheel is lagging behind one step and displaying the last value instead of the current one, is there a way to fix that?

Also, is there a better way to do multiple sliders than how I've tried it here? I just copied the code you provided and added 1 to all the values.

Code:
local ctx = reaper.ImGui_CreateContext('Slider Test', 300, 600)
local default_value = 0
local value = default_value
local default_value2 = 0
local value2 = default_value2
local reset = {}


function loop()

if reaper.ImGui_IsCloseRequested(ctx) then
reaper.ImGui_DestroyContext(ctx)
return
end

reaper.ImGui_SetNextWindowPos(ctx, 0, 0)-- Don't really need but it makes it at 0 0 "Lock it"
reaper.ImGui_SetNextWindowSize(ctx, reaper.ImGui_GetDisplaySize(ctx))-- Don't really need but it makes it the size of the screen
reaper.ImGui_Begin(ctx, 'wnd', nil, reaper.ImGui_WindowFlags_NoDecoration())


--- GUI HERE
 local rv
 local rv2

  -- ...

  rv,value = myVSliderInt('##v1', 40, 160, value, -24, 24, default_value)
  if rv then
    dosomething()
  end
  
  
  rv2,value2 = myVSliderInt2('##v2', 40, 160, value2, -24, 24, default_value2)
  if rv2 then
    dosomething()
  end
  

  -- ...
  
reaper.ImGui_End(ctx)
reaper.defer(loop)
end

function dosomething()
reaper.ShowConsoleMsg(value)
end

function resetOnDoubleClick(id, value, default)
  if reaper.ImGui_IsItemDeactivated(ctx) and reset[id] then
    reset[id] = nil
    return default
  elseif reaper.ImGui_IsItemHovered(ctx) and reaper.ImGui_IsMouseDoubleClicked(ctx, 0) then
    reset[id] = true
  end
  return value
end

function applyMousewheel(value, min, max)
  local shift = reaper.ImGui_GetKeyMods(ctx) & reaper.ImGui_KeyModFlags_Shift() ~= 0
  local wheel, hwheel = reaper.ImGui_GetMouseWheel(ctx)
  if shift and wheel == 0 then wheel = hwheel end
  if wheel ~= 0 then
    speed = shift and 1 or 1
    value = value + math.ceil(wheel * speed)
    dosomething()
    return math.max(min, math.min(value, max))
  end
  return value
end

function myVSliderInt(id, width, height, value, min, max, default_value)
  local rv
  rv,value = reaper.ImGui_VSliderInt(ctx, id, width, height, value, min, max)
  value = resetOnDoubleClick(id, value, default_value)
  if reaper.ImGui_IsItemHovered(ctx) then
    value = applyMousewheel(value, min, max)
  end
  -- or ImGui_IsItemDeactivatedAfterEdit to only do something if the value changed
  return reaper.ImGui_IsItemDeactivated(ctx), value
end

function myVSliderInt2(id, width, height, value2, min, max, default_value)
  local rv2
  rv2,value2 = reaper.ImGui_VSliderInt(ctx, id, width, height, value2, min, max)
  value2 = resetOnDoubleClick(id, value2, default_value)
  if reaper.ImGui_IsItemHovered(ctx) then
    value2 = applyMousewheel(value2, min, max)
  end
  -- or ImGui_IsItemDeactivatedAfterEdit to only do something if the value changed
  return reaper.ImGui_IsItemDeactivated(ctx), value2
end

loop()
Arthur McArthur is offline   Reply With Quote
Old 03-10-2021, 12:18 AM   #24
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Quote:
Originally Posted by Arthur McArthur View Post
Here's a bit of code testing it out, it seems like the mousewheel is lagging behind one step and displaying the last value instead of the current one, is there a way to fix that?
That is because the dosomething() inside applyMousewheel happens before the global value variable is modified.

You could do something like this:

Code:
function applyMousewheel(value, min, max)
  -- ...
    return true, math.max(min, math.min(value, max))
  end
  
  return false, value
end

function myVSliderInt(id, width, height, value, min, max, default_value)
  local rv
  rv,value = reaper.ImGui_VSliderInt(ctx, id, width, height, value, min, max)
  value = resetOnDoubleClick(id, value, default_value)
  local changed
  if reaper.ImGui_IsItemHovered(ctx) then
    changed,value = applyMousewheel(value, min, max)
  end
  if not changed then changed = reaper.ImGui_IsItemDeactivated(ctx) end
  -- or ImGui_IsItemDeactivatedAfterEdit to only do something if the value changed
  return changed, value
end
Quote:
Originally Posted by Arthur McArthur View Post
Also, is there a better way to do multiple sliders than how I've tried it here? I just copied the code you provided and added 1 to all the values.
There's no need to add another myVSliderInt* function for each slider. It can be reused:

Code:
  rv,value = myVSliderInt('##v1', 40, 160, value, -24, 24, default_value)
  if rv then
    dosomething()
  end
  
  
  rv,value2 = myVSliderInt('##v2', 40, 160, value2, -24, 24, default_value2)
  if rv then
    dosomething()
  end
cfillion is offline   Reply With Quote
Old 03-10-2021, 05:49 AM   #25
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,690
Default

Any chance for this in JSFX -> @gfx ?

-Michael
mschnell is offline   Reply With Quote
Old 03-10-2021, 07:23 AM   #26
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Quote:
Originally Posted by mschnell View Post
Any chance for this in JSFX -> @gfx ?
That's not possible with the current REAPER extension API.
cfillion is offline   Reply With Quote
Old 03-10-2021, 07:33 AM   #27
Arthur McArthur
Human being with feelings
 
Arthur McArthur's Avatar
 
Join Date: Sep 2016
Location: Toronto
Posts: 744
Default

Great, thank you!!

Here's some test slider example code based on what cfillon provided for those interested, it has:

-Integer and double sliders in horizontal and vertical
-Default values (double-click to reset)
-Mousewheel (shift-mousewheel for fine adjustments)
-Different mousewheel sensitivity variables for integer and double

Code:
local ctx = reaper.ImGui_CreateContext('Slider Test', 250, 250)

int_mousewheel_sensitivity = 1
int_mousewheel_sensitivity_fine = 1
double_mousewheel_sensitivity = .1
double_mousewheel_sensitivity_fine = .01

local default_value = 0
local value = default_value
local default_value2 = 1
local value2 = default_value2
local default_value3 = 2
local value3 = default_value3
local default_value4 = 3
local value4 = default_value4

local reset = {}

----------------------------- TEST FUNCTION ----------------------

function dosomething()
reaper.ShowConsoleMsg(test_input .. '\n')
end

-------------------------------------------------------------------
------------------------- GUI  ------------------------------------
-------------------------------------------------------------------

function loop()

if reaper.ImGui_IsCloseRequested(ctx) then
reaper.ImGui_DestroyContext(ctx)
return
end

reaper.ImGui_SetNextWindowPos(ctx, 0, 0)-- Don't really need but it makes it at 0 0 "Lock it"
reaper.ImGui_SetNextWindowSize(ctx, reaper.ImGui_GetDisplaySize(ctx))-- Don't really need but it makes it the size of the screen
reaper.ImGui_Begin(ctx, 'wnd', nil, reaper.ImGui_WindowFlags_NoDecoration())

 local rv
 
  ---Vertical Slider
  rv,value = myVSliderInt('##v1', 40, 160, value, -10, 10, default_value)
  if rv then
    test_input = value
    dosomething()
  end
  
  reaper.ImGui_SameLine(ctx)
  
  ---Vertical Slider Double
  rv,value2 = myVSliderDouble('##v2', 40, 160, value2, -10, 10, default_value2)
  if rv then
    test_input = value2
    dosomething()
  end
  
  ---Horizontal Slider
  rv,value3 = mySliderInt('##v3', value3, -10, 10, default_value3)
  if rv then
    test_input = value3
    dosomething()
  end
  
  ---Horizontal Slider Double
  rv,value4 = mySliderDouble('##v4', value4, -10, 10, default_value4)
  if rv then
    test_input = value4
    dosomething()
  end
  
reaper.ImGui_End(ctx)
reaper.defer(loop)

end

--------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------
----------------------------  DOUBLE CLICK AND MOUSEWHEEL FUNCTIONS   --------------------------------

function resetOnDoubleClick(id, value, default)
  if reaper.ImGui_IsItemDeactivated(ctx) and reset[id] then
    reset[id] = nil
    return default
  elseif reaper.ImGui_IsItemHovered(ctx) and reaper.ImGui_IsMouseDoubleClicked(ctx, 0) then
    reset[id] = true
  end
  return value
end

function applyMousewheel(value, min, max)
  local shift = reaper.ImGui_GetKeyMods(ctx) & reaper.ImGui_KeyModFlags_Shift() ~= 0
  local wheel, hwheel = reaper.ImGui_GetMouseWheel(ctx)
  if shift and wheel == 0 then wheel = hwheel end

  if wheel ~= 0 then
    speed = shift and int_mousewheel_sensitivity_fine or int_mousewheel_sensitivity
    value = value + math.ceil(wheel * speed)
    return true, math.max(min, math.min(value, max)) 
  end
  rv=true
  return false, value
end

function applyMousewheelDouble(value, min, max)
  local shift = reaper.ImGui_GetKeyMods(ctx) & reaper.ImGui_KeyModFlags_Shift() ~= 0
  local wheel, hwheel = reaper.ImGui_GetMouseWheel(ctx)
  if shift and wheel == 0 then wheel = hwheel end
  if wheel ~= 0 then
    speed = shift and double_mousewheel_sensitivity_fine or double_mousewheel_sensitivity
    value = value + (wheel * speed)
    return true, math.max(min, math.min(value, max)) 
  end
  rv=true
  return false, value
  
end
  
------------------------------------     VERTICAL SLIDER          ---------------------------------

function myVSliderInt(id, width, height, value, min, max, default_value)
  local rv
  rv,value = reaper.ImGui_VSliderInt(ctx, id, width, height, value, min, max)
  value = resetOnDoubleClick(id, value, default_value)
  local changed
  if reaper.ImGui_IsItemHovered(ctx) then
    changed,value = applyMousewheel(value, min, max)
  end
  if not changed then changed = reaper.ImGui_IsItemDeactivatedAfterEdit(ctx) end
  return changed, value
end

------------------------------------     VERTICAL SLIDER DOUBLE         ---------------------------------

function myVSliderDouble(id, width, height, value, min, max, default_value)
  local rv
  rv,value = reaper.ImGui_VSliderDouble(ctx, id, width, height, value, min, max)
  value = resetOnDoubleClick(id, value, default_value)
  local changed
  if reaper.ImGui_IsItemHovered(ctx) then
    changed,value = applyMousewheelDouble(value, min, max)
  end
  if not changed then changed = reaper.ImGui_IsItemDeactivatedAfterEdit(ctx) end
  return changed, value
end

-----------------------------------------    HORIZONTAL SLIDER       ---------------------------

function mySliderInt(id, value, min, max, default_value)
  local rv
  rv,value = reaper.ImGui_SliderInt(ctx, id, value, min, max)
  value = resetOnDoubleClick(id, value, default_value)
  local changed
  if reaper.ImGui_IsItemHovered(ctx) then
    changed, value = applyMousewheel(value, min, max)
  end
  if not changed then changed = reaper.ImGui_IsItemDeactivatedAfterEdit(ctx) end
  return changed, value
end

-----------------------------------------    HORIZONTAL SLIDER DOUBLE      ---------------------------

function mySliderDouble(id, value, min, max, default_value)
  local rv
  rv,value = reaper.ImGui_SliderDouble(ctx, id, value, min, max)
  value = resetOnDoubleClick(id, value, default_value)
  local changed
  if reaper.ImGui_IsItemHovered(ctx) then
    changed, value = applyMousewheelDouble(value, min, max)
  end
  if not changed then changed = reaper.ImGui_IsItemDeactivatedAfterEdit(ctx) end
  return changed, value
end

----------------------------------------------------------------------------
-----------------------------------------------------------------------------
--------------------------------------------------------------------------

loop()
Arthur McArthur is offline   Reply With Quote
Old 03-10-2021, 10:26 AM   #28
Edgemeal
Human being with feelings
 
Edgemeal's Avatar
 
Join Date: Apr 2016
Location: ASU`ogacihC
Posts: 3,913
Default

Interesting!
Edgemeal is offline   Reply With Quote
Old 03-10-2021, 02:33 PM   #29
aurelien
Human being with feelings
 
Join Date: Apr 2014
Posts: 95
Default

Awesome !
Did some testing with the full demo and multiple selectables: it seems the ImGui_KeyModFlags are not working: when i disable it, i am able to select multiple lines.

line 1112
Code:
    if r.ImGui_TreeNode(ctx, 'Selection State: Multiple Selection') then
      demo.HelpMarker('Hold CTRL and click to select multiple items.')
      for i,sel in ipairs(widgets.selectables.multiple) do
        if r.ImGui_Selectable(ctx, ('Object %d'):format(i-1), sel) then
          -- if not (r.ImGui_GetKeyMods(ctx) & r.ImGui_KeyModFlags_Ctrl()) ~= 0 then -- Clear selection when CTRL is not held
            -- for j = 1, #widgets.selectables.multiple do
              -- widgets.selectables.multiple[j] = false
            -- end
          -- end
          widgets.selectables.multiple[i] = not sel
        end
      end
      r.ImGui_TreePop(ctx)
    end
aurelien is offline   Reply With Quote
Old 03-10-2021, 09:17 PM   #30
gxray
Human being with feelings
 
Join Date: Dec 2020
Location: Miami, FL USA
Posts: 396
Default

Have been studying basic C++ the past two days to try to integrate some of these third party prebuilt ImGui components with ReaImGui.

It's been entirely thanks to @cfillion

Today was first success!
Accomplished the following:
  • Learned more basic C++ (aka bothered cfillion all day)
  • Ported 36 ImGui styles/skins to ReaImgui
  • Ported a style-inherited "toggle" button
  • Ported two curve editor components
  • The top one works but I'm unsure how useful it will be
  • The bottom one where you see nothing, is much more useful, but I'm too stupid to figure out how to get it working right. Something for tomorrow I guess.

Tomorrow, I will try to port:
  • A visual node-based editor for DSP/FX graphs or routing stuff
  • A sequencer/arranger timeline component
  • Also need to write some docs on how folks can help port these and integrate other ones

Demo of today's fruits:

__________________
Seasoned codemonkey
Dunno a thing about making music (here to learn!)
gxray is offline   Reply With Quote
Old 03-11-2021, 06:17 AM   #31
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Quote:
Originally Posted by aurelien View Post
Awesome !
Did some testing with the full demo and multiple selectables: it seems the ImGui_KeyModFlags are not working: when i disable it, i am able to select multiple lines.

line 1112
The commented out lines are responsible for disabling multi-selection when Ctrl is not down. However the condition is incorrect, it should have been:

Code:
if (r.ImGui_GetKeyMods(ctx) & r.ImGui_KeyModFlags_Ctrl()) == 0 then -- Clear selection when CTRL is not held"
(Fixed for the next release)
cfillion is offline   Reply With Quote
Old 03-11-2021, 06:39 AM   #32
aurelien
Human being with feelings
 
Join Date: Apr 2014
Posts: 95
Default

Thanks cfillion !

I commented out the lines to test multi-selection in the example i posted.
With your fix, everything is working as expected!

I'm really grateful for your work.
aurelien is offline   Reply With Quote
Old 03-11-2021, 09:16 AM   #33
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 1,992
Default

Quote:
Originally Posted by gxray View Post
Have been studying basic C++ the past two days to try to integrate some of these third party prebuilt ImGui components with ReaImGui.

It's been entirely thanks to @cfillion

Today was first success!
Accomplished the following:
  • Learned more basic C++ (aka bothered cfillion all day)
  • Ported 36 ImGui styles/skins to ReaImgui
  • Ported a style-inherited "toggle" button
  • Ported two curve editor components
  • The top one works but I'm unsure how useful it will be
  • The bottom one where you see nothing, is much more useful, but I'm too stupid to figure out how to get it working right. Something for tomorrow I guess.

Tomorrow, I will try to port:
  • A visual node-based editor for DSP/FX graphs or routing stuff
  • A sequencer/arranger timeline component
  • Also need to write some docs on how folks can help port these and integrate other ones

Demo of today's fruits:

Wow! This will end up being even bigger than what already is ! Danke! a lot mr.gxray!

Even a timeline and a node based !!!

This is other level GUI for scripters now
daniellumertz is online now   Reply With Quote
Old 03-14-2021, 04:56 PM   #34
Arthur McArthur
Human being with feelings
 
Arthur McArthur's Avatar
 
Join Date: Sep 2016
Location: Toronto
Posts: 744
Default

Is there any way to suppress the error:

Loading presetImGui assertion failed: (g.CurrentWindowStack.Size == 1) && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"

When a ReaScript error pops up?
Arthur McArthur is offline   Reply With Quote
Old 03-16-2021, 05:21 AM   #35
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Quote:
Originally Posted by Arthur McArthur View Post
Is there any way to suppress the error:

Loading presetImGui assertion failed: (g.CurrentWindowStack.Size == 1) && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"

When a ReaScript error pops up?
No, because those messages are meant to inform that a fatal ImGui error occured and the context has been destroyed.

If preventing Lua errors from happening during the script's normal operation is not possible, you could use Lua's protected mode (pcall/xpcall functions) to catch them without crashing the script mid-frame.

https://www.lua.org/pil/8.4.html
cfillion is offline   Reply With Quote
Old 03-16-2021, 04:34 PM   #36
X-Raym
Human being with feelings
 
X-Raym's Avatar
 
Join Date: Apr 2013
Location: France
Posts: 9,875
Default




Such things could be huge game changer for theming!


Default windows popup is doesn't real time preview...

also making copy color from other element could be done way more easily...


This could be a good framework to remake the theme tweaker!
X-Raym is offline   Reply With Quote
Old 03-16-2021, 04:59 PM   #37
_Stevie_
Human being with feelings
 
_Stevie_'s Avatar
 
Join Date: Oct 2017
Location: Black Forest
Posts: 5,054
Default

Goodness X-Raym, such a great idea!
__________________
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 03-16-2021, 05:20 PM   #38
X-Raym
Human being with feelings
 
X-Raym's Avatar
 
Join Date: Apr 2013
Location: France
Posts: 9,875
Default

@Stevie
And it is getting better such quickly !!





Copy paste of colors for theme tweaker, at last!
X-Raym is offline   Reply With Quote
Old 03-16-2021, 06:27 PM   #39
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 1,992
Default

ahhhh great idea indeed! copy and paste will save a lot of time hahaha
daniellumertz is online now   Reply With Quote
Old 03-17-2021, 02:47 AM   #40
_Stevie_
Human being with feelings
 
_Stevie_'s Avatar
 
Join Date: Oct 2017
Location: Black Forest
Posts: 5,054
Default

Waaaah, do you plan to release that script?
__________________
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
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 08:58 AM.


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