Jump to content


Photo

[Solved] Help: slider to navigate tracks. Problems notifier and? _obse

slider tracks notifier _observable

  • Please log in to reply
34 replies to this topic

#1 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 13 December 2016 - 16:28

Hi

 

I'm completing some tweaks from my tool GT16-Colors. But there are details that resist:

 

slider-tracks.png

 

Below the tool are two bars. The longest (right) is to navigate between tracks, the index (all, tracks, groups, master and sends).

 

I have a code that works fine, but with a problem that I can not solve. The code:

-----------------------------------------------
-- _OBSERVABLE, NOTIFIER
-----------------------------------------------
function ini_counters()
  local song = renoise.song()
  vb.views["nav_track"].value = song.selected_track_index --bar selected track
end

function counters()
  local song = renoise.song()
  song.selected_track_index_observable:add_notifier(ini_counters)
end

renoise.tool().app_new_document_observable:add_notifier(counters)

---------------------------------------------------
-- THE SLIDER (Piece of the GUI)
---------------------------------------------------
vb:slider {
  id = 'nav_track',
  min = 1,
  max = renoise.song().sequencer_track_count + 1 + renoise.song().send_track_count, -- number all tracks
  value = renoise.song().selected_track_index, -- selected track
  width = 530,
  notifier = function(val) 
    renoise.song().selected_track_index = math.floor(val)
  end
},

What happens:

  1. With this code, I can navigate with the slider without errors, from the tool. OK!
  2. With this code, I can navigate with pattern editor/matrix/mixer without errors, from the Renoise. OK!
  3. With this code, by including or deleting anyone track, when navigating between tracks, returns an error like this:
*** std::logic_error: 'ViewBuilder: invalid value for slider: '49'. value must be [1 - 48].'
*** stack traceback:
***   [C]: ?
***   [C]: in function '__newindex'
***   [string "do..."]:22: in function <[string "do..."]:9>
***   main.lua:720: in function <main.lua:698>

What code is missing to avoid this error? Is the notifier of slider incorrect?  Or the error is higher with the _observables???

 

When Renoise adds or removes a track, the range changes, the "max" of slider must change, but the tool does not update it.

 

Joule have a tool (joule.no0b.Transporter.xrnx) related with this, but it serves to navigate between patterns (the sequence), and is more complex.

 

I only intend to operate a simple slider to navigate. I guess to navigate between patterns would be similar...

 

What would be the correct code?


Edited by Raul (ulneiz), 17 December 2016 - 16:29.

:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#2 joule

joule

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1516 posts
  • Gender:Not Telling
  • Location:Sweden
  • Interests:music, philosophy, engineering

Posted 13 December 2016 - 17:04

(I'm a bit limited answering from my phone) :)

Most likely: in ini_counters(), before updating the .value, insert a line that updates the slider .max value.

 

EDIT:

function ini_counters()
  local song = renoise.song()
  vb.views.nav_track.max = #song.tracks
  vb.views.nav_track.value = song.selected_track_index --bar selected track
end

PS. Some nitpicking.. I suspect your slider will only go to the last track on its very far right position. This should be possible to solve/optimize with some maths, handling the range as tracks+1 positions. Maybe not a big deal... hmm... simplest is perhaps to increase the max by 1, and then do a math.floor(val-0.01) actually.


Edited by joule, 13 December 2016 - 17:38.


#3 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 13 December 2016 - 18:58

(I'm a bit limited answering from my phone) :)

Most likely: in ini_counters(), before updating the .value, insert a line that updates the slider .max value.

 

EDIT:

function ini_counters()
  local song = renoise.song()
  vb.views.nav_track.max = #song.tracks
  vb.views.nav_track.value = song.selected_track_index --bar selected track
end

PS. Some nitpicking.. I suspect your slider will only go to the last track on its very far right position. This should be possible to solve/optimize with some maths, handling the range as tracks+1 positions. Maybe not a big deal... hmm... simplest is perhaps to increase the max by 1, and then do a math.floor(val-0.01) actually.

 

Thanks! Ok, Now it works perfectly. But I have a problem associated with the start of Renoise:

slider-error-start-renoise.png

 

The line that causes it is: max = #renoise.song().tracks 

          vb:horizontal_aligner {
            width = 570,
            vb:slider {
              id = 'nav_track',
              min = 1,
              max = #renoise.song().tracks, -- number all tracks  <------------------
              value = renoise.song().selected_track_index, -- selected track
              width = 530,
              notifier = function(val) 
                renoise.song().selected_track_index = math.floor(val)
              end
            },
            vb:text {
              id = 'index_value',
              text = '---'
            },
            vb:text {
              id = 'col_value',
              text = '---'
            }
          }

Is the first line that contain "renoise.song()" inside the vb: .

 

How do you avoid this error when starting Renoise using "renoise.song()" inside the vb:?

 

After starting Renoise, I pulse "Tools/Reload all Tools", and the tool works perfectly. What is missing?


:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#4 joule

joule

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1516 posts
  • Gender:Not Telling
  • Location:Sweden
  • Interests:music, philosophy, engineering

Posted 13 December 2016 - 20:26

You should never reference renoise.song() in the "outer scope" of your tool. This is because all tools are run when renoise starts = before renoise.song() exists.

 

Whatever you do with vb there, put it inside a function instead, e g create_vb_content(). Then call that from your renoise.app():show_custom_dialog call. It's also common practice to have a main() or init() function triggered by app_new_document_observable.

 

A related example, just to show the principle

song = nil

function main()
  song = renoise.song() -- now available from all functions, since it was initially declared in the outermost scope.
  create_vb_content() -- Or rather, I'd really call this from/via the renoise.app():show_custom_dialog() call. Don't bloat the memory by creating a GUI that might not be shown.
  -- Better: local dialog = renoise.app():show_custom_dialog("Title", create_vb_content())
end
 
function create_vb_content()
  local vb = renoise.Viewbuilder()
  local my_content = vb:text { text = "hello" }
  return my_content
end
 
renoise.tool().app_new_document_observable:add_notifier(main)

Edited by joule, 13 December 2016 - 20:30.


#5 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 14 December 2016 - 01:18



 

You should never reference renoise.song() in the "outer scope" of your tool. This is because all tools are run when renoise starts = before renoise.song() exists.

 

Whatever you do with vb there, put it inside a function instead, e g create_vb_content(). Then call that from your renoise.app():show_custom_dialog call. It's also common practice to have a main() or init() function triggered by app_new_document_observable.

 

A related example, just to show the principle

song = nil

function main()
  song = renoise.song() -- now available from all functions, since it was initially declared in the outermost scope.
  create_vb_content() -- Or rather, I'd really call this from/via the renoise.app():show_custom_dialog() call. Don't bloat the memory by creating a GUI that might not be shown.
  -- Better: local dialog = renoise.app():show_custom_dialog("Title", create_vb_content())
end
 
function create_vb_content()
  local vb = renoise.Viewbuilder()
  local my_content = vb:text { text = "hello" }
  return my_content
end
 
renoise.tool().app_new_document_observable:add_notifier(main)

 

Thanks!

 

I followed your instructions, but now I have problems with the "id" of multiple tabs. Errors as this:

*** std::logic_error: 'ViewBuilder: a view with the id 'attab_t' was already registered to this viewbuilder instance.'
*** stack traceback:
***   [C]: in function 'column'
***   main.lua:905: in function 'gt16_content'
***   main.lua:1712: in function <main.lua:1707>

Should all functions be within the main(), with a "local"?

 

Now the tool appears to load before Renoise, and his GUI is displayed, below Renoise. I think something is still wrong with the load.

 

Would you mind examining my main.lua and fixing the bug? I would like you to try the tool. All other files you can ignore. The problem is inside "main.lua" file. This is something that is driving me crazy.

 

The tool: Attached File  ulneiz.GT16-Colors_v1.1.xrnx   74.99KB   34 downloads (notice to readers: this does not work correctly)

 

The all code of main.lua:

--[[----------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------
--
-- TOOL NAME: GT16-Colors
-- VERSIÓN: 1.2 (for R3.1)
-- COMPATIBILITY: Windows, Mac, Linux (32/64bit)
-- AUTHOR: ulneiz (Spain)
-- DATE: Summer 2016
--
--------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------]]--

--------------------------------------------------------------------------------
-- INITIALS
--------------------------------------------------------------------------------
song = nil
dialog = nil -- Placeholder for the dialog
vb = renoise.ViewBuilder() -- ViewBuilder for show_dialog() function & others

class 'RenoiseScriptingTool' ( renoise.Document.DocumentNode )
  function RenoiseScriptingTool:__init()    
    renoise.Document.DocumentNode.__init(self) 
    self:add_property( 'Name' , 'Untitled Tool' )
    self:add_property( 'Version', '1.2')
    self:add_property( 'Id' , 'Unknown Id' )
  end

manifest = RenoiseScriptingTool()
ok,err = manifest:load_from( 'manifest.xml' )
tool_name = manifest:property( 'Name' ).value
tool_id = manifest:property( 'Id' ).value
tool_version = manifest:property('Version').value


--------------------------------------------------------------------------------
-- CONSTANTS
--------------------------------------------------------------------------------
--TAB_COM = 1

ATTAB_F = 1
ATTAB_T = 2

TAB_MGR = 1
TAB_GRP = 2
TAB_SGR = 3
TAB_TRK = 4
TAB_CMB = 5

SGRTAB_SGR = 1
SGRTAB_OCT = 2

SUBTAB_MGR = 1
SUBTAB_GRP = 2
SUBTAB_SGR = 3
SUBTAB_TRK = 4

HLPTAB_CLS = 1
HLPTAB_HLP = 2
HLPTAB_ABO = 3
HLPTAB_TLS = 4

TLSTAB_01 = 1
TLSTAB_02 = 2
TLSTAB_03 = 3
TLSTAB_04 = 4
TLSTAB_05 = 5
TLSTAB_06 = 6
TLSTAB_07 = 7
TLSTAB_08 = 8
TLSTAB_09 = 9
TLSTAB_10 = 10


F , T = false , true

--------------------------------------------------------------------------------
-- INCLUDES
--------------------------------------------------------------------------------
require ("tools/tool_01")
require ("tools/tool_02")
require ("tools/tool_03")
require ("tools/tool_04")
require ("tools/tool_05")
require ("tools/tool_06")
require ("tools/tool_07")
require ("tools/tool_08")
require ("tools/tool_09")
require ("tools/tool_10")


--------------------------------------------------------------------------------
-- UTILITY FUNCTIONS
--------------------------------------------------------------------------------
function LOG(...) print (...) end -- log print


--------------------------------------------------------------------------------
-- TABS NAVIGATION
--------------------------------------------------------------------------------

-- ATTAB Control------------------
-- show/update the indicated tab
function show_attab( idx )
  LOG( 'show_attab()' , idx )
  vb.views.attab_t.visible = ( idx == ATTAB_T ) and true or false
  vb.views.attab_f.visible = ( idx == ATTAB_F ) and true or false
end

-- next/previous attab
function show_next_attab()
  local new_attab = visible_attab
  if ( visible_attab == ATTAB_F ) then
    new_attab = ATTAB_T
    else
    new_attab = new_attab + 1
  end
  vb.views.at_switch.value = new_attab
end

function show_previous_attab()
  local new_attab = visible_attab
  if ( visible_attab == ATTAB_T ) then
    new_attab = ATTAB_F
    else
    new_attab = new_attab - 1
  end
  vb.views.at_switch.value = new_attab
end

-- force update of current attab
function update_attab()
  local attab_idx = visible_attab
  visible_attab = 0
  show_attab( attab_idx )
end



-- TAB Control------------------
-- show/update the indicated tab
function show_tab( idx )
  LOG( 'show_tab()' , idx )
  --vb.views.tab_com.visible = ( idx == TAB_COM ) and true or false
  vb.views.tab_mgr.visible = ( idx == TAB_MGR ) and true or false
  vb.views.tab_grp.visible = ( idx == TAB_GRP ) and true or false
  vb.views.tab_sgr.visible = ( idx == TAB_SGR ) and true or false
  vb.views.tab_trk.visible = ( idx == TAB_TRK ) and true or false
  vb.views.tab_cmb.visible = ( idx == TAB_CMB ) and true or false
end

-- next/previous tab
function show_next_tab()
  local new_tab = visible_tab
  if ( visible_tab == TAB_CMB ) then
    new_tab = TAB_MGR
    else
    new_tab = new_tab + 1
  end
  vb.views.main_switch.value = new_tab
end

function show_previous_tab()
  local new_tab = visible_tab
  if ( visible_tab == TAB_MGR ) then
    new_tab = TAB_CMB
    else
    new_tab = new_tab - 1
  end
  vb.views.main_switch.value = new_tab
end

-- force update of current tab
function update_tab()
  local tab_idx = visible_tab
  visible_tab = 0
  show_tab( tab_idx )
end

-- SGRTAB Control------------------
-- show/update the indicated tab
function show_sgrtab( idx )
  LOG( 'show_sgrtab()' , idx )
  vb.views.sgrtab_sgr.visible = ( idx == SGRTAB_SGR ) and true or false
  vb.views.sgrtab_oct.visible = ( idx == SGRTAB_OCT ) and true or false
end

-- next/previous sgrtab
function show_next_sgrtab()
  local new_sgrtab = visible_sgrtab
  if ( visible_sgrtab == SGRTAB_OCT ) then
    new_sgrtab = SGRTAB_SGR
    else
    new_sgrtab = new_sgrtab + 1
  end
  vb.views.main_switch.value = new_sgrtab
end

function show_previous_sgrtab()
  local new_sgrtab = visible_sgrtab
  if ( visible_sgrtab == SGRTAB_SGR ) then
    new_sgrtab = SGRTAB_OCT
    else
    new_sgrtab = new_sgrtab - 1
  end
  vb.views.main_switch.value = new_sgrtab
end

-- force update of current sgrtab
function update_sgrtab()
  local sgrtab_idx = visible_sgrtab
  visible_sgrtab = 0
  show_sgrtab( sgrtab_idx )
end



-- SUBTAB Control------------------
-- show/update the indicated subtab
function show_subtab( idx )
  LOG( 'show_subtab()' , idx )
  vb.views.subtab_mgr.visible = ( idx == SUBTAB_MGR ) and true or false
  vb.views.subtab_grp.visible = ( idx == SUBTAB_GRP ) and true or false
  vb.views.subtab_sgr.visible = ( idx == SUBTAB_SGR ) and true or false
  vb.views.subtab_trk.visible = ( idx == SUBTAB_TRK ) and true or false
end 

-- next/previous subtab
function show_next_subtab()
  local new_subtab = visible_subtab
  if ( visible_subtab == SUBTAB_TRK ) then
    new_subtab = SUBTAB_MGR
  else
    new_subtab = new_subtab + 1
  end
  vb.views.submain_switch.value = new_subtab
end

function show_previous_subtab()
  local new_subtab = visible_subtab
  if ( visible_subtab == SUBTAB_MGR ) then
    new_subtab = SUBTAB_TRK
    else
    new_subtab = new_subtab - 1
  end
  vb.views.submain_switch.value = new_subtab
end

-- force update of current subtab
function update_subtab()
  local subtab_idx = visible_subtab
  visible_subtab = 0
  show_subtab( subtab_idx )
end


-- HLPTAP Control------------------
-- show/update the indicated subtab
function show_hlptab( idx )
  LOG( 'show_hlptab()' , idx )
  vb.views.hlptab_cls.visible = ( idx == HLPTAB_CLS ) and true or false
  vb.views.hlptab_hlp.visible = ( idx == HLPTAB_HLP ) and true or false
  vb.views.hlptab_abo.visible = ( idx == HLPTAB_ABO ) and true or false
  vb.views.hlptab_tls.visible = ( idx == HLPTAB_TLS ) and true or false
end 

-- next/previous hlptab
function show_next_hlptab()
  local new_hlptab = visible_hlptab
  if ( visible_hlptab == HLPTAB_TLS ) then
    new_hlptab = HLPTAB_CLS
    else
    new_hlptab = new_hlptab + 1
  end
  vb.views.help_switch.value = new_hlptab
end

function show_previous_hlptab()
  local new_hlptab = visible_hlptab
  if ( visible_hlptab == HLPTAB_CLS ) then
    new_hlptab = HLPTAB_TLS
    else
    new_hlptab = new_hlptab - 1
  end
  vb.views.help_switch.value = new_hlptab
end

-- force update of current hlptab
function update_hlptab()
  local hlptab_idx = visible_hlptab
  visible_hlptab = 0
  show_hlptab( hlptab_idx )
end


-- TLSTAB Control------------------
-- show/update the indicated tlstab
function show_tlstab( idx )
  LOG( 'show_tlstab()' , idx )
  vb.views.tlstab_01.visible = ( idx == TLSTAB_01 ) and true or false
  vb.views.tlstab_02.visible = ( idx == TLSTAB_02 ) and true or false
  vb.views.tlstab_03.visible = ( idx == TLSTAB_03 ) and true or false
  vb.views.tlstab_04.visible = ( idx == TLSTAB_04 ) and true or false
  vb.views.tlstab_05.visible = ( idx == TLSTAB_05 ) and true or false
  vb.views.tlstab_06.visible = ( idx == TLSTAB_06 ) and true or false
  vb.views.tlstab_07.visible = ( idx == TLSTAB_07 ) and true or false
  vb.views.tlstab_08.visible = ( idx == TLSTAB_08 ) and true or false
  vb.views.tlstab_09.visible = ( idx == TLSTAB_09 ) and true or false
  vb.views.tlstab_10.visible = ( idx == TLSTAB_10 ) and true or false
end 

-- next/previous tlstab
function show_next_tlstab()
  local new_tlstab = visible_tlstab
  if ( visible_tlstab == TLSTAB_10 ) then
    new_tlstab = TLSTAB_01
  else
    new_tlstab = new_tlstab + 1
  end
  vb.views.tools_switch.value = new_tlstab
end

function show_previous_tlstab()
  local new_tlstab = visible_tlstab
  if ( visible_tlstab == TLSTAB_01 ) then
    new_tlstab = TLSTAB_10
  else
    new_tlstab = new_tlstab - 1
  end
  vb.views.tools_switch.value = new_tlstab
end

-- force update of current tlstab
function update_tlstab()
  local tlstab_idx = visible_tlstab
  visible_tlstab = 0
  show_tlstab( tlstab_idx )
end









--------------------------------------------------------------------------------
-- NAVIGATION BARS
--------------------------------------------------------------------------------
function show_cbox_bars( idx )
  LOG( 'show_cbox_bars()' , idx )
  vb.views.cbox_bars_t.visible = ( idx == true )
  vb.views.cbox_bars_f.visible = ( idx == false )
end

checkbox_bars = vb:checkbox { id = 'cbox_bars', value = true, tooltip = 'Show/Hide the Bars bellow', notifier = function(new_idx) show_cbox_bars(new_idx) end }


--------------------------------------------------------------------------------
-- BUTTONS, SWITHES & POPUPS
--------------------------------------------------------------------------------


  swi_cl_atmenu =  vb:switch { items = {'☱', '⇲' }, id = 'at_switch', tooltip = '⇲ Close Up  [MOUSE CLICK]\n☱  Controls  [MOUSE CLICK]',
                               notifier = function(new_index) show_attab(new_index) end, width = 59, height = 20, value = 1 }  -- show_attab(2)


  swi_cl_hmenu =   vb:switch { items = { '⇱', '☲K', '?', '⚒' }, id = 'help_switch', tooltip = '⇱ Close Down  [CTRL + END]\n☲K Key Commands  [CTRL + PRIOR]\n? About  [CTRL + NEXT]\n⚒ Advanced Tools [CTRL + INS]',
                               notifier = function(new_index) show_hlptab(new_index) end, width = 102, height = 24, value = 4 } -- show_hlptab(1)



  swi_cl_tmenu =   vb:switch { items = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}, id = 'tools_switch', tooltip = 'Advanced Tools Area \n[CTRL + 1,2...9,0]\n&\nSPNO with SGr Octaves\n[CTRL+SHIFT + 2]',
                               notifier = function(new_index) show_tlstab(new_index) end, width = 300, height = 20, value = 4 }  -- show_tlstab(1)



-- Insert Buttons ( 5 + 4 ) --------------------
------------------------------------------------
--Insert MGr
function insert_01()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    mgr_select ( swi_cl_mgr.value , mgroup.value )
    renoise.app():show_status( "GT16-Colors:  your selected Main Group has been inserted in Pattern Editor." )
    else renoise.app():show_status( "⚠ GT16-Colors:  please, select before a Track or Group in Pattern Editor or Mixer!!!" )
  end
end

--Insert Gr
function insert_02()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    gr_select ( swi_cl_gr.value , group.value )
    renoise.app():show_status( "GT16-Colors:  your selected Group/s has been inserted in Pattern Editor." )
    else renoise.app():show_status( "⚠ GT16-Colors:  please, select before a Track or Group in Pattern Editor or Mixer!!!" )
  end
end

--Insert SGr
function insert_03()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    sgr_select ( swi_cl_sgr.value , sgroup.value )
    renoise.app():show_status( "GT16-Colors:  your selected SubGroups has been inserted in Pattern Editor." )
  else renoise.app():show_status( "⚠ GT16-Colors:  please, select before a Track or Group in Pattern Editor or Mixer!!!" )
  end
end

--Insert Oct
function insert_oct()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    oct_select ( swi_cl_oct.value , octave.value )
    renoise.app():show_status( "GT16-Colors:  your selected Octaves SubGroup has been inserted in Pattern Editor." )
  else renoise.app():show_status( "⚠ GT16-Colors:  please, select before a Track or Group in Pattern Editor or Mixer!!!" )
  end
end

--Insert Tr
function insert_04()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then 
    tr_select ( swi_cl_tr.value , track.value )
    renoise.app():show_status( "GT16-Colors:  your selected Track/s has been inserted in Pattern Editor." )
  else renoise.app():show_status( "⚠ GT16-Colors:  please, select before a Track or Group in Pattern Editor or Mixer!!!" )
  end
end

  
--Combined...
--Insert Cmb MGr
function insert_05()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    mgr_com ( mgrcom.value )
    renoise.app():show_status( "GT16-Colors:  your selected Combined Main Groups has been inserted in Pattern Editor." )
    else renoise.app():show_status( "⚠ GT16-Colors:  please, select before a Track or Group in Pattern Editor or Mixer!!!" )
  end
end

--Insert Cmb Gr
function insert_06()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    gr_com ( grcom.value )
    renoise.app():show_status( "GT16-Colors:  your selected Combined Groups has been inserted in Pattern Editor." )
    else renoise.app():show_status( "⚠ GT16-Colors:  please, select before a Track or Group in Pattern Editor or Mixer!!!" )
  end
end
  
--Insert Cmb SGr
function insert_07()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    sgr_com ( sgrcom.value )
    renoise.app():show_status( "GT16-Colors:  your selected Combined SubGroups has been inserted in Pattern Editor." )
    else renoise.app():show_status( "⚠ GT16-Colors:  please, select before a Track or Group in Pattern Editor or Mixer!!!" )
  end
end
  
--Insert Cmb Tr
function insert_08()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    tr_com( trcom.value )
    renoise.app():show_status( "GT16-Colors:  your selected Combined Tracks has been inserted in Pattern Editor." )
    else renoise.app():show_status( "⚠ GT16-Colors:  please, select before a Track or Group in Pattern Editor or Mixer!!!" )
  end
end


-- Lower Buttons ( x14 ) ------------------------
-------------------------------------------------
--Swap, Undo/Redo, NavTracks, Del & First/Last
function navt_swap_left()
  local song = renoise.song()
  --print (song.selected_track_index , song.sequencer_track_count - song.sequencer_track_count + 1)
  if ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER and song.selected_track_index > 1 ) then
    song.selected_track_index = song.selected_track_index - 1
    if ( song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP ) then
      local ssid = song.tracks[ song.selected_track_index ]
      local m = #ssid.members + 2
      song.selected_track_index = song.selected_track_index + 1
      song:swap_tracks_at( song.selected_track_index, song.selected_track_index - m )
      song.selected_track_index = song.selected_track_index - m
    elseif ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) then
      song.selected_track_index = song.selected_track_index + 1
      song:swap_tracks_at( song.selected_track_index, song.selected_track_index - 1 )
      song:select_previous_track()
    end
    renoise.app():show_status( "GT16-Colors:  your selected Track has been swaped to left." )
    else renoise.app():show_status( "⚠ GT16-Colors:  please, for Swap select before a Track in Pattern Editor or Mixer!!!" )
  end
end
---
function navt_swap_right()
  local song = renoise.song()
  --print (song.selected_track_index , song.sequencer_track_count)
  if ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER and song.selected_track_index < song.sequencer_track_count ) then
    song.selected_track_index = song.selected_track_index + 1
    if ( song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP and song.selected_track_index < song.sequencer_track_count ) then
      song.selected_track_index = song.selected_track_index - 1
      song:swap_tracks_at(song.selected_track_index, song.selected_track_index + 2 )
      song.selected_track_index = song.selected_track_index + 2
    elseif ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) then
      song.selected_track_index = song.selected_track_index - 1
      song:swap_tracks_at(song.selected_track_index, song.selected_track_index + 1 )
      song:select_next_track()
    end
    renoise.app():show_status( "GT16-Colors:  your selected Track has been swaped to right." )
    else renoise.app():show_status( "⚠ GT16-Colors:  please, for Swap select before a Track in Pattern Editor or Mixer!!!" )

  end    
end
---
function navt_coll_gr( A )
  local song = renoise.song()
  local selected_track = song.tracks[ song.selected_track_index ]
  if ( selected_track.type == renoise.Track.TRACK_TYPE_GROUP ) then
    for i = 1, #selected_track.members do
      A = selected_track.members[ i ]
          if ( A.collapsed == T ) then A.collapsed = F -- False is expanded
        renoise.app():show_status( "GT16-Colors:  selected Group expanded." )
      elseif ( A.collapsed == F ) then A.collapsed = T -- True is collapsed
          renoise.app():show_status( "GT16-Colors:  selected Group collapsed." )
      end
    end
  end
  if ( selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) or
     ( selected_track.type == renoise.Track.TRACK_TYPE_MASTER ) or
   ( selected_track.type == renoise.Track.TRACK_TYPE_SEND ) then
    if selected_track.collapsed == T then song.tracks[song.selected_track_index].collapsed = F
    if ( selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) then
      renoise.app():show_status( "GT16-Colors:  selected Track expanded." )
      end
    if ( selected_track.type == renoise.Track.TRACK_TYPE_MASTER ) then
      renoise.app():show_status( "GT16-Colors:  selected Master expanded." )
      end
    if ( selected_track.type == renoise.Track.TRACK_TYPE_SEND ) then
      renoise.app():show_status( "GT16-Colors:  selected Send expanded." )
    end
    elseif selected_track.collapsed == F then song.tracks[song.selected_track_index].collapsed = T
    if ( selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) then
      renoise.app():show_status( "GT16-Colors:  selected Track colapsed." )
    end
    if ( selected_track.type == renoise.Track.TRACK_TYPE_MASTER ) then
      renoise.app():show_status( "GT16-Colors:  selected Master colapsed." )
    end
    if ( selected_track.type == renoise.Track.TRACK_TYPE_SEND ) then
      renoise.app():show_status( "GT16-Colors:  selected Send colapsed." )
    end
    end
  end  
end
---
function navt_on_off( A )
  local song = renoise.song()
  if ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) or 
     ( song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP ) or
     ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEND ) then
    A = song.tracks[song.selected_track_index].mute_state
  end
  if A == 1 then song.tracks[song.selected_track_index]:mute()
    if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) then
      renoise.app():show_status( "GT16-Colors:  selected Track Off." )
  end
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
      renoise.app():show_status( "GT16-Colors:  selected Group Off." )
  end
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEND) then
      renoise.app():show_status( "GT16-Colors:  selected Send Off." )
  end
  elseif A == 2 then song.tracks[song.selected_track_index]:unmute()
    if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) then
      renoise.app():show_status( "GT16-Colors:  selected Track Play." )
  end
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
      renoise.app():show_status( "GT16-Colors:  selected Group Play." )
  end
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEND) then
      renoise.app():show_status( "GT16-Colors:  selected Send Play." )
  end
  else 
  renoise.app():show_status( "GT16-Colors:  please, for Play/Off select before a Track/Group/Send in Pattern Editor or Mixer!!!" )
  end
end
---
function navt_undo()
  local song = renoise.song()
  song:undo()
end
function navt_redo()
  local song = renoise.song()
  song:redo()
end
---
function navt_01()
  local song = renoise.song()
  if song.selected_track_index > 2 then
    song:select_previous_track() song:select_previous_track()
  renoise.app():show_status( "GT16-Colors: x2 previous Track/Group selected." )
  end  
end
function navt_02()
  local song = renoise.song()
  if song.selected_track_index > 1 then
    song:select_previous_track()
  renoise.app():show_status( "GT16-Colors:  previous Track/Group selected." )
  end
end
function navt_03()
  local song = renoise.song()
  if song.selected_track_index < song.sequencer_track_count - 1 then
    song:select_next_track() song:select_next_track()
  renoise.app():show_status( "GT16-Colors:  x2 next Track/Group selected." )
  end
end
function navt_04()
  local song = renoise.song()
  if song.selected_track_index < song.sequencer_track_count then
    song:select_next_track()
  renoise.app():show_status( "GT16-Colors:  next Track/Group selected." )
  end
end
---
function navt_del()
  local song = renoise.song()
  if ( song.sequencer_track_count == 1 ) then
  elseif ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) or
         ( song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP ) then
    song:delete_track_at( song.selected_track_index )
    renoise.app():show_status( "GT16-Colors:  selected Track/Group deleted." )
  end     
  if ( song.sequencer_track_count + song.send_track_count  == 1 ) then
  elseif ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEND ) then
    song:delete_track_at( song.selected_track_index )
    renoise.app():show_status( "GT16-Colors:  selected send deleted." )
  end
  if ( song.selected_track.type == renoise.Track.TRACK_TYPE_MASTER ) then
    renoise.app():show_status( "GT16-Colors:  Master is not deletable! Please, for delete select before a Track/Group/Send in Pattern Editor or Mixer!!!" )
  end 
  if ( song.selected_track_index == 1 ) and ( song.sequencer_track_count == 1 ) then
  renoise.app():show_status( "GT16-Colors:  only a Track or Group in Pattern Editor. Include more Tracks and delete after!" )      
  end
end
---
function navt_rem( A , B )
  local song = renoise.song()
  A = song.sequencer_track_count
  B = song.send_track_count
  for A = 2 , song.sequencer_track_count do
    if ( ( song.sequencer_track_count <= 99 ) and ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) or ( song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP ) ) then
      song:delete_track_at( song.selected_track_index ) ---- LIMIT = 99 ---------|***
      renoise.app():show_status( "GT16-Colors:  all Groups & Tracks removed inside Pattern Editor, least the first. Start again!" )
    end
  end
  for B = 1, song.sequencer_track_count + song.send_track_count do
    if ( ( song.send_track_count <= 99 ) and ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEND ) ) then
      song:delete_track_at( song.selected_track_index ) ---- LIMIT = 99 ---------|***
    end  
  end
  if A == 1 and ( song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) or ( song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP ) then
    renoise.app():show_status( "GT16-Colors:  only a Track or Group in Pattern Editor. Include more Tracks and remove after!" )
  end
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_MASTER) then
    renoise.app():show_status( "GT16-Colors:  master is not deletable! Please, for delete select before a Track/Group/Send in Pattern Editor or Mixer!!!" )
  end 
end
---
function navt_first()
  local song = renoise.song()
  song.selected_track_index = song.sequencer_track_count - song.sequencer_track_count + 1 
  renoise.app():show_status( "GT16-Colors:  jumped to first Track/Group." )
end
function navt_last()
  local song = renoise.song()
  song.selected_track_index = song.sequencer_track_count
  renoise.app():show_status( "GT16-Colors:  jumped to last Track/Group." )
end
---
function navs_up()
  local song = renoise.song()
  local sid = song.selected_sequence_index
  if song.selected_sequence_index > 1 then
    song.selected_sequence_index = sid - 1 --select next pattern
    renoise.app():show_status( "GT16-Colors:  previous Pattern." )
  end
end
function navs_down()
  local song = renoise.song()
  local sid = song.selected_sequence_index
  if #song.sequencer.pattern_sequence > ( sid ) then
    song.selected_sequence_index = sid + 1 --select next pattern
    renoise.app():show_status( "GT16-Colors:  next Pattern." )
  end
end
---
function play()
  local song = renoise.song()
  local play_mode = renoise.Transport.PLAYMODE_RESTART_PATTERN
  song.transport:start(play_mode)
  renoise.app():show_status( "GT16-Colors:  Song play." )
end
function stop()
  local song = renoise.song()
  song.transport:stop()
  renoise.app():show_status( "GT16-Colors:  Song stop." )
end

-- Show Counters in real time --
-------------------------------------------------
function ini_counters()
  local song = renoise.song()
  vb.views["pt_index"].text = ( "Idx [%s" ):format( song.selected_sequence_index ) --selected pattern
  vb.views["tr_index"].text = ( "x %s]" ):format( song.selected_track_index ) --selected track  
  --- ---
  vb.views["pt_count"].text = ( " Tt [%s" ):format( #song.sequencer.pattern_sequence ) --total patterns
  vb.views["tr_count"].text = ( "x %s]" ):format( song.sequencer_track_count ) --total tracks
  --- ---
  vb.views["tr_name"].text = ( "%s" ):format( song.tracks[song.selected_track_index].name ) --Name selected track
  vb.views["tr_color"].color = ( song.tracks[song.selected_track_index].color ) --Name selected track
  --color
  --- ---
  if song.sequencer_track_count <= 48 then
    vb.views["bar_count"].color = { 0x00,0xFF,0x2A } --green
    elseif song.sequencer_track_count >= 49 and song.sequencer_track_count <= 96 then
    vb.views["bar_count"].color = { 0xFF,0x80,0x00 } --orange
    elseif song.sequencer_track_count >= 97 then
    vb.views["bar_count"].color = { 0xFF,0x00,0x00 } --red
  end
  --- ---
  vb.views["tr_send"].text = ( "Snd %s" ):format( song.send_track_count )--Total Sends
  --- ---
  vb.views.nav_track.max = #song.tracks
  vb.views["nav_track"].value = song.selected_track_index --bar selected track
  
end

function counters()
  local song = renoise.song()
  song.selected_sequence_index_observable:add_notifier(ini_counters) 
  song.selected_track_index_observable:add_notifier(ini_counters)
  song.sequencer.pattern_sequence_observable:add_notifier(ini_counters)
  song.tracks[song.selected_track_index].name_observable:add_notifier(ini_counters)
  song.tracks[song.selected_track_index].color_observable:add_notifier(ini_counters)
end
renoise.tool().app_new_document_observable:add_notifier(counters) -- To initialice New Start Renoise




-- Process Buttons for Advanced Tools (x10) -----
-------------------------------------------------

function process_03()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    tool_03 ()
  end
end
function process_04()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    tool_04 ()
  end
end
function process_05()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    tool_05 ()
  end
end
function process_06()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    tool_06 ()
  end
end
function process_07()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    tool_07 ()
  end
end
function process_08()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    tool_08 ()
  end
end
function process_09()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    tool_09 ()
  end
end
function process_10()
  local song = renoise.song()
  if (song.selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER) or (song.selected_track.type == renoise.Track.TRACK_TYPE_GROUP) then
    tool_10 ()
  end
end
  

  
-- Text (Key Commands & About) -----------------
------------------------------------------------

txt_K00 = vb:text {
text =
"* ALT+CTRL + G = Invoke GT16-Colors. Assign manually this Key Command in 'Preferences/Keys... Global/Tools/GT16-Colors'"
}

txt_K01 = vb:text {
text =
"F1 = 'Main Groups' Switch\n"..
"F2 = 'Groups' Switch\n"..
"F3 = 'SubGroups' Switch\n"..
"F4 = 'Tracks' Switch\n"..
"SHIFT + F3 = 'Octaves' Switch\n"..
"\n"..
"← → = Left / Right Select Color\n"..
"↑ ↓ = Up / Down Select Distribution\n"..
"\n"..
"CTRL + PRIOR =  ☲K  Keys info\n"..
"CTRL + NEXT =  ?  About GT16-Colors\n"..
"\n"..
"CTRL + INS =  ⚒  Advanced Tools\n"..
"CTRL + 1,2...9,0 =  Tools Switch\n"..
"CTRL+SHIFT + 2 = SPNO Expand"
}

txt_K02 = vb:text {
text =
"F5 = 'Cmb ...Main Groups' Switch\n"..
"F6 = 'Cmb ...Groups' Switch\n"..
"F7 = 'Cmb ...SubGroups' Switch\n"..
"F8 = 'Cmb ...Tracks' Switch\n"..
"\n"..
"CTRL + Z =  ↩  Undo\n"..
"CTRL + Y =  ↪  Redo\n"..
"\n"..
"CTRL+SHIFT + K =  |≡◄  Coll./Exp. Tr/Gr\n"..
"CTRL+SHIFT + J =  |≡◄  Coll./Exp. Tr in Gr\n"..
"Ç =  ☊  Mute/Unmute Selected Tr/Gr\n"..
"\n"..
"CTRL + HOME =  ⇱  Compact Mode\n"..
"CTRL + END =  ⇱  ... ☲K  ?  ⚒  Closed\n"..
"ESC =  ✖  Exit Window"
}

txt_K03 = vb:text {
text =
"F9   =  |◄  First Tr/Gr Switch\n"..
"F10 =  ◄  Previous Tr/Gr Switch\n"..
"F11 =  ►  Next Tr/Gr Switch\n"..
"F12 =  ►|  Last Tr/Gr Switch\n"..
"\n"..
"CTRL + F9   =  ↶|  Swap Selected Tr to Left\n"..
"CTRL + F10 =  ◄◄  Previous Tr/Gr x2 Switch\n"..
"CTRL + F11 =  ►►  Next Tr/Gr x2 Switch\n"..
"CTRL + F12 =  |↷  Swap Selected Tr/Gr to Right\n"..
"\n"..
"CTRL+SHIFT + DEL = Remove all Tr/Gr\n"..
"CTRL + DEL = Delete Tr/Gr\n"..
"\n"..
"ALT + SPACE = Insert Selection\n"..
"ALT + RETURN = Process SPNO"
}


txt_A01 = vb:multiline_text {
width = 684, height = 130, text = 
"'GT16-Colors' is a free MultiTool created by ulneiz for Renoise, to build & customice various distributions/combinations of 'Groups & Tracks per Colors' for Pattern Editor & Mixer.\n"..
"There are '16 Colors' with until 1200 Options to choose and multiple Tool Modules for control. Select a 'Color', select a 'Distribution' or 'Combination' and build according your needs. With GT16-Colors, the groups are the protagonists!\n"..
"\n"..
"Use only a floating window to control your building. Use the tabs to enlarge or reduce the window. Use the integrated key commands, your mouse or both for maximum control. Use the aditional controls below of each Advanced Tool to discover a new ocean... \n"..
"\n"..
"⚠ Some 'Combined Options' includes many Groups & Tracks (>100), to insert only with a click of mouse or key command.\n"..
"Please, use with moderation!!! Think before using...\n"..
"\n"..
"⚒ Moreover, GT16-Colors include until ten bottom slots to control & edit (Advanced Tools section). This version 1.2 include two powerful tools, 'GTC' (magnificent for control) & 'SPNO' (effective for sort pitches of notes per octaves) integrated in the slots one & two. Apart, if you know the LUA code language, you can add your personal tools here (eight free slots).\n"..
"\n"..
"This version 1.2 of GT16-Colors is tested with Renoise 3.1.0 and work fine.\n"..
"\n"..
"Enjoy your Renoise!   Enjoy your GT16-Colors!\n"..
"\n"..
"                     ✉ To contact ulneiz: visit the Renoise Forums, www.renoise.com"
}

txt_A02 = vb:text { text = "℗ 2016. ulneiz  (Spain)" }



--------------------------------------------------------------------------------
-- GT16_CONTENT (GUI)
--------------------------------------------------------------------------------
function gt16_content()

local content =
vb:column {
  margin = 4,
  --tabs Controls------------
  vb:column {
    id = 'attab_t',
    visible = false,
  },
  ------------------------------------------------------------------------------ GT16-Colors
  vb:column {
    id = 'attab_f',
    visible = false,
    --menu-------------------------------------
    vb:column {
      style = 'group',  --body
      margin = 4,
      --values------------------------------------
      vb:horizontal_aligner {
        mode = 'left',
        vb:horizontal_aligner {
          --width= 600,
          vb:horizontal_aligner { --Pattern Sequence
            width = 110,
            margin = 2,
            vb:column { 
              tooltip = 'Number of Index Group/Track Selected.\nSelect any Track to reactivate!',
              width = 110,
              style = 'body',
              vb:horizontal_aligner {
                height = 21,
                mode = 'center',
                vb: text {
                  id = 'pt_index',
                  align = 'right',
                  font = 'bold',
                  text = 'Index'
                },
                vb: text {
                  id = 'tr_index',
                  align = 'left',
                  font = 'bold',                  
                  text = '[Y x X]'
                }
              }
            }
          },
          vb:horizontal_aligner { width = 6 },
          vb:horizontal_aligner { --Name Track
            width = 220,
            margin = 2,
            vb:column {
              tooltip = 'Number of Index Group/Track Selected.\nSelect any Track to reactivate!',
              width = 220,
              style = 'body',
              vb:horizontal_aligner {
                height = 21,
                mode = 'center',
                vb: text {
                  id = 'tr_name',
                  align = 'center',
                  font = 'bold',
                  text = 'Name of Track'
                }
              }
            }
          },
          vb:button { --Counter Tracks Marker
            active = F,
            id = 'tr_color',  -- ID
            color = { 0x20,0x00,0x00 }, -- black 
            tooltip = 'Color Track',
            height = 25,
            width = 24,
          },
          vb:horizontal_aligner { width = 4 },
          vb:horizontal_aligner { --Counter Tracks/Groups
            width = 110,
            margin = 2,
            vb:column {
              tooltip = 'Total Count of Groups & Tracks.\nSelect any Track to reactivate!',
              width = 110,
              style = 'body',
              vb:horizontal_aligner {
                height = 21,
                mode = 'center',
                vb: text {
                  id = 'pt_count',
                  align = 'right',
                  font = 'bold',                  
                  text = 'Total'
                },
                vb: text {
                  id = 'tr_count',
                  align = 'left',
                  font = 'bold',
                  text = '[Y x X]'
                }
              }
            }
          },
          vb:button { --Counter Tracks Marker
            active = F,
            id = 'bar_count',  -- ID
            color = { 0x00,0x30,0x00 }, -- black 
            tooltip = 'Green: Low Level of Members (until 48).\nOrange: Middle Level of Members (49 until 96).\nRed: High Level of Members (more of 96).',
            height = 25,
            width = 7
          },
          vb:horizontal_aligner { width = 4 },
          vb:horizontal_aligner { --Counter Send
            tooltip = 'Total Number of Send Tracks.\nSelect any Track to reactivate!',
            width = 60,
            margin = 2,
            vb:column {
              width = 60,
              style = 'body',
              vb:horizontal_aligner {
                height = 21,
                mode = 'center',
                vb: text {
                  id = 'tr_send',
                  align = 'center',
                  font = 'bold',                  
                  text = 'Sends'
                }
              }
            }
          },
          vb:horizontal_aligner { width = 104 },
          vb:column {
            vb:horizontal_aligner {
              vb:text { text = 'Bars ' },
              checkbox_bars --bars
            }
          }
        }
      },    
      --NavTracks-------------------------------
      vb:horizontal_aligner {
        swi_cl_hmenu, --hmenu
        vb:horizontal_aligner { width = 4 },
        vb:button {
          color = { 0xAA,0x6A,0x00 },
          text = '↩',
          id = 'navt_undo',
          tooltip = 'Undo  \n[CTRL + Z]',
          height = 24,
          width = 40,
          notifier = function()navt_undo() end
        },
        vb:button {
          color = { 0xAA,0x6A,0x00 },
          text = '↪',
          id = 'navt_redo',
          tooltip = 'Redo  \n[CTRL + Y]',
          height = 24,
          width = 40,
          notifier = function()navt_redo() end
        },
        vb:horizontal_aligner { width = 4 },
        vb:button {
          color = { 0x66,0x00,0x00 },
          text = 'REM',
          id = 'navt_cln',
          tooltip = 'Remove all Tracks/Groups inside Pattern Editor (least the first) & Start Again!\n[CTRL+SHIFT + DEL]\n⚠ Limited until 99 members. All expanded!',
          height = 24,
          width = 40,
          notifier = function()navt_rem() end
        },
        vb:button {
          color = { 0x66,0x00,0x00 },
          text = 'DEL',
          id = 'navt_del',
          tooltip = 'Delete Selected Track/Group/Send\n[CTRL + DEL]',
          height = 24,
          width = 40,
          notifier = function()navt_del() end
        },
        vb:horizontal_aligner { width = 4 },
        vb:button {
          color = { 0x06,0x3B,0x70 },
          text = '☊',
          id = 'note_on_off',
          tooltip = "'Play/Off' Selected Track/Group/Send  (Master not included)\n[Ç]",
          height = 24,
          width = 30,
          notifier = function() navt_on_off() end
        },
        vb:button {
          color = { 0x7F,0x05,0x54 },
          text = '|≡◄ ',
          id = 'collapse_expand_gr',
          tooltip = "'Collapse/Expand' all Tracks inside the Group Parent\n[CTRL+SHIFT + J]\nor\n'Collapse/Expand' Selected Track/Master/Send\n[CTRL+SHIFT + K]",
          height = 24,
          width = 40,
          notifier = function() navt_coll_gr() end
        },
        vb:horizontal_aligner { width = 4 },
        vb:button {
          color = { 0x00,0x20,0x50 },
          text = '↶|',
          id = 'navt_swap_left',
          tooltip = 'Swap Selected Track to Left\n[CTRL + F9]',
          height = 24,
          width = 30,
          notifier = function()navt_swap_left() end
        },
        vb:button {
          color = { 0x00,0x20,0x50 },
          text = '|↷',
          id = 'navt_swap_right',
          tooltip = 'Swap Selected Track to Right\n[CTRL + F12]',
          height = 24,
          width = 30,
          notifier = function()navt_swap_right() end
        },
        vb:horizontal_aligner { width = 4 },
        vb:button {
          color = { 0x4F,0x0C,0x89 },
          text = '|◄',
          id = 'navt_first',
          tooltip = 'First Track/Group \n[F9]',
          height = 24,
          width = 30,
          notifier = function()navt_first() end
        },
        vb:button {
          color = { 0x4F,0x0C,0x89 },
          text = '◄◄  ◄',
          id = 'navt_02',
          tooltip = 'Previous Track/Group\n[F10]\nor\nPrevious Track/Group x2\n[CTRL + F10]',
          height = 24,
          width = 50,
          notifier = function()navt_02() end
        },
        vb:button {
          color = { 0x4F,0x0C,0x89 },
          text = '►  ►►',
          id = 'navt_03',
          tooltip = 'Next Track/Group\n[F11]\nor\nNext Track/Group x2\n[CTRL + F11]',
          height = 24,
          width = 50,
          notifier = function()navt_04() end
        },
        vb:button {
          color = { 0x4F,0x0C,0x89 },
          text = '►|',
          id = 'navt_last',
          tooltip = 'Last Track/Group\n[F12]',
          height = 24,
          width = 30,
          notifier = function()navt_last() end
        },
        vb:button {
          color = { 0x40,0x00,0x80 },
          text = '▲',
          id = 'navs_up',
          tooltip = 'Up sequence \n[]',
          height = 24,
          width = 30,
          notifier = function()navs_up() end
        },
        vb:button {
          color = { 0x40,0x00,0x80 },
          text = '▼',
          id = 'navs_down',
          tooltip = 'Down sequence\n[]',
          height = 24,
          width = 30,
          notifier = function()navs_down() end
        },
        vb:horizontal_aligner { width = 4 },
        vb:button {
          color = { 0x00,0x60,0x00 },
          text = '▶',
          id = 'play',
          tooltip = 'Play the song\n[]',
          height = 24,
          width = 30,
          notifier = function()play() end
        },
        vb:button {
          color = { 0x00,0x40,0x00 },
          text = '■',
          id = 'stop',
          tooltip = 'Stop the song\n[]',
          height = 24,
          width = 30,
          notifier = function()stop() end
        },
      },
      --NavColumns - NavTracks-------------------------------
      vb:column {
        id ='cbox_bars_f',
        visible = false,
      },  
      vb:column {
        id = 'cbox_bars_t',
        visible = false,
        vb:horizontal_aligner { height = 5 },
        vb:horizontal_aligner {
          mode = 'justify',
          vb:horizontal_aligner {
            width = 120,
            vb:slider {
              id = 'nav_col',
              min = 0, --1,
              max = 1, --12, --renoise.song().tracks[ renoise.song().selected_track_index ].visible_note_columns
              value = 0, --1,
              width = 120,
              -- notifier = function(value) renoise.song().selected_note_column_index = value end,
              notifier = function(val)
                if ( renoise.song().selected_track.type == renoise.Track.TRACK_TYPE_SEQUENCER ) then
                  local num_column =  renoise.song().tracks[ renoise.song().selected_track_index ].visible_note_columns - 1
                  local col_idx = math.floor( val * num_column ) + 1
                  renoise.song().selected_note_column_index = col_idx
                  print( 'col_idx: ' ..col_idx )
                end
              end
            }
          },
          ---
          vb:horizontal_aligner {
            width = 570,
            vb:slider {
              id = 'nav_track',
              min = 1,
              max = #song.tracks, --(renoise.song().sequencer_track_count + 1 + renoise.song().send_track_count), -- number all tracks
              value = song.selected_track_index, -- selected track
              width = 530,
              notifier = function(val) 
                song.selected_track_index = math.floor(val)
              end
            },
            vb:text {
              id = 'index_value',
              text = '---'
            },
            vb:text {
              id = 'col_value',
              text = '---'
            }
          },
          --[[------------------------------------
          vb:horizontal_aligner {
            width = 570,
            vb:slider {
              id = 'nav_track',
              min = 0, --1,
              max = 1, --2, --renoise.song().sequencer_track_count + 1 + renoise.song().send_track_count,
              value = 0, --1,
              width = 530,
              --notifier = function(value) renoise.song().selected_track_index = value end,
              notifier = function(val)
                local num_tracks =  renoise.song().sequencer_track_count + renoise.song().send_track_count
                local track_idx = math.floor( val * num_tracks ) + 1
                renoise.song().selected_track_index = track_idx
                print( 'track_idx: ' ..track_idx )
              end
            },
            vb:text {
              id = 'index_value',
              text = '---'
            },
            vb:text {
              id = 'col_value',
              text = '---'
            }
          }
          ----------------------------------]]
        }
      }
      ---
    },  
    --hlpmenu----------------------------------
    vb:column {
      visible = false,
      id = 'hlptab_cls'
    },
    ----------------------------- Keys
    vb:column {
      vb:horizontal_aligner { height = 5 },
      visible = false,
      id = 'hlptab_hlp',
      vb:column {
        style = 'group',
        margin = 5,
        vb:horizontal_aligner { 
          vb:text {
            font = 'bold',
            text = '☲K  Key Commands'
          }  
        },
        vb:horizontal_aligner { height = 5 },
        vb:column {
          style = 'body',
          width = 694,
          height = 24,
          margin = 8,
          txt_K00
        },
        vb:horizontal_aligner { height = 5 },    
        vb:horizontal_aligner {
          mode = "justify",
          vb:column {
            style = 'body',
            width = 206,
            height = '100%',
            margin = 8,
            txt_K01
          },
          vb:column {
            style = 'body',
            width = 226,
            height = '100%',
            margin = 8,
            txt_K02
          },
          vb:column {
            style = 'body',
            width = 252,
            height = '100%',
            margin = 8,
            txt_K03
          }
        }
      }
    },
    ------------------------------- About
    vb:column {
      vb:horizontal_aligner { height = 5 },
      visible = false,
      id = 'hlptab_abo',
      vb:column {
        style = 'group',
        margin = 5,
        vb:horizontal_aligner {
          mode = 'justify',
          vb:column {
            vb:text {
              font = 'bold',
              text = '? About GT16-Colors   v1.2'
            }   
          },
          vb:column {
            vb:horizontal_aligner {
              but_cm.aq, but_cm.bk, but_cm.bl, but_cm.br, but_cm.gl, but_cm.gy, but_cm.gr, but_cm.og, but_cm.pl, but_cm.pn, but_cm.ps, but_cm.rd, but_cm.sk, but_cm.vl, but_cm.wh, but_cm.yl
            }
          }
        },
        vb:horizontal_aligner { height = 5 },
        vb:column {
          style = 'body',
          height = '100%',
          margin = 5,
          txt_A01,
          vb:horizontal_aligner {
            mode = 'right',
            txt_A02
          }
        }
      }
    }
  },
  ------------------------------------------------------------------------------ GT16-Colors
  ------------------------------------- Advanced Tools (Pattern Editor)
  vb:column {
    vb:horizontal_aligner { height = 5 },
    visible = false,
    id = 'hlptab_tls',
    vb:column {  
      style = 'group',
      margin = 5,
      vb:horizontal_aligner {
        mode = 'right',
        vb:column {
          vb:horizontal_aligner {
            mode = 'right',
            vb:text {
              font = 'big',
              text = '⚒'
            },
            vb:text {
              font = 'bold',
              text = 'Advanced Tools '
            }
          }
        },
        vb:column {
        vb:horizontal_aligner {
          swi_cl_tmenu,
          vb:horizontal_aligner { width = 4 },
          swi_cl_atmenu
          }
        }
      },
      -------------------------------Tool 01
      vb:column {
        visible = false,
        id = 'tlstab_01',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---
        PEST, --Tool 01 Added!
        --- ---
      },
      -------------------------------Tool 02
      vb:column {
        visible = false,
        id = 'tlstab_02',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---
        MESS, --Tool 02 Added!
        --- ---
      },
      -------------------------------Tool 03
      vb:column {
        visible = false,
        id = 'tlstab_03',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---        
        DCSP, --Tool 03 Added!
        --- ---        
      },
      -------------------------------Tool 04
      vb:column {
        visible = false,
        id = 'tlstab_04',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---
        GTC, --Tool 04 Added!
        --- ---
      },
      -------------------------------Tool 05
      vb:column {
        visible = false,
        id = 'tlstab_05',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---
        AMIC, -- Tool 05
        --- ---
      },
      -------------------------------Tool 06
      vb:column {
        visible = false,
        id = 'tlstab_06',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---
        SPNO, -- Tool 06
        --- ---
      },
      -------------------------------Tool 07
      vb:column {
        visible = false,
        id = 'tlstab_07',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---
        TL07, -- Tool 07
        --- ---
      },
      -------------------------------Tool 08
      vb:column {
        visible = false,
        id = 'tlstab_08',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---
        TL08, -- Tool 08
        --- ---
      },
      -------------------------------Tool 09
      vb:column {
        visible = false,
        id = 'tlstab_09',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---
        TL09, -- Tool 09
        --- ---
      },
      -------------------------------Tool 10
      vb:column {
        visible = false,
        id = 'tlstab_10',
        style = 'body',
        width = 694,
        margin = 08,
        --- ---
        TL10, -- Tool 10
        --- ---
      }
      -------------------------------
    }
  }
}
------------------------------------------------------------
  show_tab(2)    -- 2, start the things [1-6]
  show_sgrtab(1) -- 1, SubGroups/Octaves [1-4]
  show_subtab(1) -- 1, Combined [1-2]
  show_hlptab(4) -- 1, Hide/Keys/?/Advancet Tools [1-4]
  show_tlstab(4) -- 4, Advanced Tools Selector 1-...-9-0 [10]
  show_attab(1)  -- 1, Show/hide Controls [1-2]
  show_cbox_bars(T) -- F, Show/hide Navigation Bars [True/False]

return content
end
 

--------------------------------------------------------------------------------
-- KEYHANDLER
--------------------------------------------------------------------------------
function keyhandler( key )
    rprint(key)   -- print the value of key to the console
    -- return key -- uncomment to pass key on to Renoise
    local choice_via_shortcut = nil
    -- Insert, Process --------------------------------------------------------------------------------
        if ( key.modifiers == 'alt' and key.name == 'space'  and swi_cl_menu.value ==  2 ) then choice_via_shortcut = 'Insert MGr' insert_01()
    elseif ( key.modifiers == 'alt' and key.name == 'space'  and swi_cl_menu.value ==  3 ) then choice_via_shortcut = 'Insert Gr'  insert_02()
    elseif ( key.modifiers == 'alt' and key.name == 'space'  and swi_cl_menu.value ==  4 and swi_cl_sgrmenu.value == 1) then choice_via_shortcut = 'Insert SGr' insert_03()
    elseif ( key.modifiers == 'alt' and key.name == 'space'  and swi_cl_menu.value ==  4 and swi_cl_sgrmenu.value == 2) then choice_via_shortcut = 'Insert Oct' insert_oct()
    elseif ( key.modifiers == 'alt' and key.name == 'space'  and swi_cl_menu.value ==  5 ) then choice_via_shortcut = 'Insert Tr'  insert_04()
  ---
    elseif ( key.modifiers == 'alt' and key.name == 'space'  and swi_cl_smenu.value == 1 ) then choice_via_shortcut = 'Insert Comb. MGr' insert_05()
    elseif ( key.modifiers == 'alt' and key.name == 'space'  and swi_cl_smenu.value == 2 ) then choice_via_shortcut = 'Insert Comb. Gr'  insert_06()
    elseif ( key.modifiers == 'alt' and key.name == 'space'  and swi_cl_smenu.value == 3 ) then choice_via_shortcut = 'Insert Comb. SGr' insert_07()
    elseif ( key.modifiers == 'alt' and key.name == 'space'  and swi_cl_smenu.value == 4 ) then choice_via_shortcut = 'Insert Comb. Tr'  insert_08()
  ---
  --elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 1 ) then choice_via_shortcut = 'Process Tool 01'  process_01()
    elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 2 ) then choice_via_shortcut = 'Process Tool 02'  process_02()
  --elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 3 ) then choice_via_shortcut = 'Process Tool 03'  process_03()
  --elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 4 ) then choice_via_shortcut = 'Process Tool 04'  process_04()
  --elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 5 ) then choice_via_shortcut = 'Process Tool 05'  process_05()
  --elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 6 ) then choice_via_shortcut = 'Process Tool 06'  process_06()
  --elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 7 ) then choice_via_shortcut = 'Process Tool 07'  process_07()
  --elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 8 ) then choice_via_shortcut = 'Process Tool 08'  process_08()
  --elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 9 ) then choice_via_shortcut = 'Process Tool 09'  process_09()
  --elseif ( key.modifiers == 'alt' and key.name == 'return' and swi_cl_tmenu.value == 10) then choice_via_shortcut = 'Process Tool 10'  process_10()
    -- Switches ------------------------------------------------------------------------------
    elseif ( key.name == 'f1' ) then choice_via_shortcut = 'MGr'  vb.views[ 'main_switch' ].value = 2 
    elseif ( key.name == 'f2' ) then choice_via_shortcut = 'Gr'   vb.views[ 'main_switch' ].value = 3
    elseif ( key.modifiers == 'shift' and key.name == 'f3' ) then choice_via_shortcut = 'Oct' vb.views[ 'main_switch' ].value = 4 vb.views[ 'subgroup_switch' ].value = 2
    elseif ( key.name == 'f3' ) then choice_via_shortcut = 'SGr'  vb.views[ 'main_switch' ].value = 4 vb.views[ 'subgroup_switch' ].value = 1
    elseif ( key.name == 'f4' ) then choice_via_shortcut = 'Tr'   vb.views[ 'main_switch' ].value = 5
    elseif ( key.name == 'f5' ) then choice_via_shortcut = 'CMGr' vb.views[ 'main_switch' ].value = 6  vb.views[ 'submain_switch' ].value = 1
    elseif ( key.name == 'f6' ) then choice_via_shortcut = 'CGr'  vb.views[ 'main_switch' ].value = 6  vb.views[ 'submain_switch' ].value = 2
    elseif ( key.name == 'f7' ) then choice_via_shortcut = 'CSGr' vb.views[ 'main_switch' ].value = 6  vb.views[ 'submain_switch' ].value = 3
    elseif ( key.name == 'f8' ) then choice_via_shortcut = 'CTr'  vb.views[ 'main_switch' ].value = 6  vb.views[ 'submain_switch' ].value = 4
    -- NavTracks, Exit, Wipe & Del -----------------------------------------------------------------
    elseif ( key.modifiers == 'control' and key.name == 'f9' )  then choice_via_shortcut = 'Swap Selected Track to Left' navt_swap_left()
    elseif ( key.name == 'f9'  ) then choice_via_shortcut = 'First Track' navt_first()
    elseif ( key.modifiers == 'control' and key.name == 'f10' )  then choice_via_shortcut = 'Previous Track x 2' navt_01()
    elseif ( key.name == 'f10' ) then choice_via_shortcut = 'Previous Track' navt_02()
    elseif ( key.modifiers == 'control' and key.name == 'f11' )  then choice_via_shortcut = 'Next Track x 2' navt_03()
    elseif ( key.name == 'f11' ) then choice_via_shortcut = 'Next Track' navt_04()
    elseif ( key.modifiers == 'control' and key.name == 'f12' )  then choice_via_shortcut = 'Swap Selected Track/Main Group to Right' navt_swap_right()
    elseif ( key.name == 'f12' ) then choice_via_shortcut = 'Last Track' navt_last()
    elseif ( key.name == 'esc' ) then choice_via_shortcut = 'Exit' dialog:close()  -- (key.name == 'esc' or key.name == 'e') -- ...or 'e'
    elseif ( key.name == 'ç' )   then choice_via_shortcut = 'Active/Off Track/Group' navt_on_off()
    elseif ( key.modifiers == 'shift + control' and key.name == 'k' ) then choice_via_shortcut = 'Collapse/Expand Track/Group' navt_coll()
    elseif ( key.modifiers == 'control' and key.name == 'z' ) then choice_via_shortcut = 'Undo' navt_undo()
    elseif ( key.modifiers == 'control' and key.name == 'y' ) then choice_via_shortcut = 'Undo' navt_redo()
    elseif ( key.modifiers == 'shift + control' and key.name == 'del' ) then choice_via_shortcut = 'Delelte' navt_rem()
    elseif ( key.modifiers == 'control' and key.name == 'del' ) then choice_via_shortcut = 'Delelte' navt_del()
    -- Close below, Keys, & About -----------------------------------------------------------------
    elseif ( key.modifiers == 'control' and key.name == 'ins' ) then choice_via_shortcut = 'Advanced Tools' vb.views[ 'help_switch' ].value = 4 vb.views[ 'main_switch' ].value = 1
    elseif ( key.modifiers == 'control' and key.name == 'home' ) then choice_via_shortcut = 'Compact Window'   vb.views[ 'main_switch' ].value = 1
    elseif ( key.modifiers == 'control' and key.name == 'end' ) then choice_via_shortcut = 'Close Window' vb.views[ 'help_switch' ].value = 1
    elseif ( key.modifiers == 'control' and key.name == 'prior' ) then choice_via_shortcut = 'Key Commands' vb.views[ 'help_switch' ].value = 2
    elseif ( key.modifiers == 'control' and key.name == 'next' ) then choice_via_shortcut = 'About' vb.views[ 'help_switch' ].value = 3
  -- Advanced Tools ------------------------------------------------------------------------
    elseif ( key.modifiers == 'shift + control' and key.name == '2' ) then choice_via_shortcut =
            'Tool 02' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 2 vb.views[ 'main_switch' ].value = 4 vb.views[ 'subgroup_switch' ].value = 2
    elseif ( key.modifiers == 'control' and key.name == '1' ) then choice_via_shortcut = 'Tool 01' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 1
    elseif ( key.modifiers == 'control' and key.name == '2' ) then choice_via_shortcut = 'Tool 02' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 2
    elseif ( key.modifiers == 'control' and key.name == '3' ) then choice_via_shortcut = 'Tool 03' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 3
    elseif ( key.modifiers == 'control' and key.name == '4' ) then choice_via_shortcut = 'Tool 04' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 4
    elseif ( key.modifiers == 'control' and key.name == '5' ) then choice_via_shortcut = 'Tool 05' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 5
    elseif ( key.modifiers == 'control' and key.name == '6' ) then choice_via_shortcut = 'Tool 06' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 6
    elseif ( key.modifiers == 'control' and key.name == '7' ) then choice_via_shortcut = 'Tool 07' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 7
    elseif ( key.modifiers == 'control' and key.name == '8' ) then choice_via_shortcut = 'Tool 08' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 8
    elseif ( key.modifiers == 'control' and key.name == '9' ) then choice_via_shortcut = 'Tool 09' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 9
    elseif ( key.modifiers == 'control' and key.name == '0' ) then choice_via_shortcut = 'Tool 10' vb.views[ 'help_switch' ].value = 4 vb.views[ 'tools_switch' ].value = 10
    end
    ----
    local vb_switch = vb.views[ 'mgr_switch_cl' ]
    if ( key.name == 'right' and swi_cl_menu.value == 2 ) then
      vb_switch.value = math.min( #vb_switch.items , vb_switch.value + 1 )
    elseif ( key.name == 'left' and swi_cl_menu.value == 2 ) then
      vb_switch.value = math.max( 1 , vb_switch.value - 1 )
    end
    local vb_popup = vb.views[ 'mgr_popup' ]
    if ( key.name == 'down' and swi_cl_menu.value == 2 ) then
      vb_popup.value = math.min(#vb_popup.items,vb_popup.value + 1 )
    elseif ( key.name == 'up' and swi_cl_menu.value == 2 ) then
      vb_popup.value = math.max( 1 , vb_popup.value - 1 )
    end
    ----
    local vb_switch = vb.views[ 'gr_switch_cl' ]
    if ( key.name == 'right' and swi_cl_menu.value == 3) then
      vb_switch.value = math.min( #vb_switch.items , vb_switch.value + 1 )
    elseif ( key.name == 'left' and swi_cl_menu.value == 3 ) then
      vb_switch.value = math.max( 1 , vb_switch.value - 1 )
    end
    local vb_popup = vb.views[ 'gr_popup' ]
    if ( key.name == 'down' and swi_cl_menu.value == 3 ) then
      vb_popup.value = math.min( #vb_popup.items,vb_popup.value + 1 )
    elseif ( key.name == 'up' and swi_cl_menu.value == 3 ) then
      vb_popup.value = math.max( 1 , vb_popup.value - 1 )
    end
    ----
    local vb_switch = vb.views[ 'sgr_switch_cl' ]
    if ( key.name == 'right' and swi_cl_menu.value == 4 and swi_cl_sgrmenu.value == 1 ) then
      vb_switch.value = math.min( #vb_switch.items , vb_switch.value + 1 )
    elseif ( key.name == 'left' and swi_cl_menu.value == 4 and swi_cl_sgrmenu.value == 1 ) then
      vb_switch.value = math.max( 1 , vb_switch.value - 1 )
    end
    local vb_popup = vb.views[ 'sgr_popup' ]
    if ( key.name == 'down' and swi_cl_menu.value == 4 and swi_cl_sgrmenu.value == 1 ) then
      vb_popup.value = math.min( #vb_popup.items , vb_popup.value + 1 )
    elseif ( key.name == 'up' and swi_cl_menu.value == 4 and swi_cl_sgrmenu.value == 1 ) then
      vb_popup.value = math.max( 1 , vb_popup.value - 1 )
    end
    ----
    local vb_switch = vb.views[ 'oct_switch_cl' ]
    if ( key.name == 'right' and swi_cl_menu.value == 4 and swi_cl_sgrmenu.value == 2 ) then
      vb_switch.value = math.min( #vb_switch.items , vb_switch.value + 1 )
    elseif ( key.name == 'left' and swi_cl_menu.value == 4 and swi_cl_sgrmenu.value == 2 ) then
      vb_switch.value = math.max( 1 , vb_switch.value - 1 )
    end
    local vb_popup = vb.views[ 'oct_popup' ]
    if ( key.name == 'down' and swi_cl_menu.value == 4 and swi_cl_sgrmenu.value == 2 ) then
      vb_popup.value = math.min( #vb_popup.items , vb_popup.value + 1 )
    elseif ( key.name == 'up' and swi_cl_menu.value == 4 and swi_cl_sgrmenu.value == 2 ) then
      vb_popup.value = math.max( 1 , vb_popup.value - 1 )
    end
    ----
    local vb_switch = vb.views[ 'tr_switch_cl' ]
    if ( key.name == 'right' and swi_cl_menu.value == 5 ) then
      vb_switch.value = math.min( #vb_switch.items , vb_switch.value + 1 )
    elseif ( key.name == 'left' and swi_cl_menu.value == 5 ) then
      vb_switch.value = math.max( 1 , vb_switch.value - 1 )
    end
    local vb_popup = vb.views[ 'tr_popup' ]
    if ( key.name == 'down' and swi_cl_menu.value == 5 ) then
      vb_popup.value = math.min( #vb_popup.items , vb_popup.value + 1 )
    elseif ( key.name == 'up' and swi_cl_menu.value == 5 ) then
      vb_popup.value = math.max( 1 , vb_popup.value - 1 )
    end

    ----

    local vb_popup = vb.views[ 'cmgr_popup' ]
    if ( key.name == 'down' and swi_cl_smenu.value == 1 ) then
      vb_popup.value = math.min( #vb_popup.items , vb_popup.value + 1 )
    elseif ( key.name == 'up' and swi_cl_smenu.value == 1 ) then
      vb_popup.value = math.max( 1 , vb_popup.value - 1 )
    end
    ----
    local vb_popup = vb.views[ 'cgr_popup' ]
    if ( key.name == 'down' and swi_cl_smenu.value == 2 ) then
      vb_popup.value = math.min( #vb_popup.items , vb_popup.value + 1 )
    elseif ( key.name == 'up' and swi_cl_smenu.value == 2 ) then
      vb_popup.value = math.max( 1 , vb_popup.value - 1 )
    end
    ----
    local vb_popup = vb.views[ 'csgr_popup' ]
    if ( key.name == 'down' and swi_cl_smenu.value == 3 ) then
      vb_popup.value = math.min( #vb_popup.items , vb_popup.value + 1 )
    elseif ( key.name == 'up' and swi_cl_smenu.value == 3 ) then
      vb_popup.value = math.max( 1 , vb_popup.value - 1 )
    end
    ----
    local vb_popup = vb.views[ 'ctr_popup' ]
    if ( key.name == 'down' and swi_cl_smenu.value == 4 ) then
      vb_popup.value = math.min( #vb_popup.items,vb_popup.value + 1 )
    elseif ( key.name == 'up' and swi_cl_smenu.value == 4 ) then
      vb_popup.value = math.max( 1 , vb_popup.value - 1 )
    end
    ----
    local vb_switch = vb.views[ 'tls_switch_cl' ]
    if ( key.name == 'right' and swi_cl_hmenu.value == 4 and swi_cl_tmenu.value == 2 ) then
      vb_switch.value = math.min( #vb_switch.items , vb_switch.value + 1 )
    elseif ( key.name == 'left' and swi_cl_hmenu.value == 4 and swi_cl_tmenu.value == 2 ) then
      vb_switch.value = math.max( 1 , vb_switch.value - 1)
    end
  end


--------------------------------------------------------------------------------
-- SHOW_DIALOG
--------------------------------------------------------------------------------
function show_dialog()
  song = renoise.song()
  
  if dialog and dialog.visible then dialog:show() return end -- Prevents duplicate of the floating window ( when reaching this, the dialog never was created before or was closed )
  
  dialog = renoise.app():show_custom_dialog ( ' '.. tool_name .. '  v' .. tool_version .. '     by: ulneiz' , gt16_content() , keyhandler )
 
  renoise.app():show_status('GT16-Colors is charged... Enjoy the colors and control!')
end

renoise.tool().app_new_document_observable:add_notifier(show_dialog)

-- Reload the script whenever this file is saved. Additionally, execute the attached function.
-- _AUTO_RELOAD_DEBUG = function() show_dialog() end
-- _AUTO_RELOAD_DEBUG = true


--------------------------------------------------------------------------------
-- MENU ENTRIES
--------------------------------------------------------------------------------
renoise.tool():add_menu_entry {
  name = 'Main Menu:Tools:'..tool_name..'... [* ALT + CTRL + G]',
  invoke = show_dialog
}

renoise.tool():add_menu_entry {
  name = 'Pattern Editor:'..tool_name..'... [* ALT + CTRL + G]',
  invoke = show_dialog
}

renoise.tool():add_menu_entry {
  name = 'Mixer:'..tool_name..'... [* ALT + CTRL + G]',
  invoke = show_dialog
}

--------------------------------------------------------------------------------
-- KEY BINDING (RECOMENDED CTRL + ALT + G)
--------------------------------------------------------------------------------
renoise.tool():add_keybinding {
  name = 'Global:Tools:' ..tool_name..'... [* ALT + CTRL + G]',
  invoke = show_dialog
}

--------------------------------------------------------------------------------
-- MIDI/KEY MAPPING
--------------------------------------------------------------------------------
--[[
renoise.tool():add_midi_mapping {
  name = 'Tools:GT16-Colors:Clean All values in Track [Trigger]',                   -- MIDI MAPPING
  invoke = function()tool_01_33() end
}
]]


--[[ UNICODE SYMBOLS

 ✓ ✔ ⇅ ⇆ ← → ↑ ↓ ↆ ❙❙❙ ☲ ❘❘❘ ☰ ⎓ ➤ ❤ ✦ ✉ ▬ ⚫ ◤ ◢ ◆ ┃ ℗ Ⓒ ➥ ✘ ✖ ⬒ ⬓ ▦ ⯬ ⇱ ☲K ? ⚒ ⚯ ☊ ☋ ♞
 
 ≡◄ ▲ ▼  ◄ ◄◄ ►► ► ◂ ⏹ ⏯ ⏵⏸⏴ ⏪ ⏩ ⏮ ⏭ ☚ ☛ ↩ ↪ ↶ ↷ 🎹 ⚒ ☠ ☢ ☣ ⚠ ☺ ⧗ 🗗 🗕 ⇱ ⇲ ☊ ↹ ♬ ⭍ ♆

 http://unicode-table.com/en/#optical-character-recognition

--]]

Could you fix it? It's what I need for the tool to work fine (organize properly, and load details). Before adding the slider, everything worked fine, tabs (switches) included. Maybe I have an organizational problem or some line of code is missing to avoid the type error: " 'ViewBuilder: a view with the id '????' was already registered to this viewbuilder instance."

 

I suspect there is something wrong in SHOW DIALOG... and in the general organization inside main.lua.

 

I apologize for asking this! (The code is long and boring). Normally, I fix the things.

 

Thank you very much!


:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#6 4Tey

4Tey

    Chief Above Chief Member

  • Normal Members
  • PipPipPipPipPipPip
  • 388 posts
  • Gender:Male

Posted 14 December 2016 - 07:24

Raul, don't ask me to explain it as I don't understand Renoise lua myself.


Edited by 4Tey, 29 December 2016 - 10:06.


#7 joule

joule

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1516 posts
  • Gender:Not Telling
  • Location:Sweden
  • Interests:music, philosophy, engineering

Posted 14 December 2016 - 08:43

Some related advice:

 

I've started using the following approach. I don't know if it is the neatest way of doing things, but these guidelines seem very practical to me:

 

1) Only use an ID if you really have to. E g, when you either have to 1) get/set a vb:object property from some other place, 2) add/remove an object at some later time.

2) Very handy thing to avoid some headache and the common "was already added" error when using ID's.

vb.views.my_text = nil -- practical stuff
vb:text {
  id = "my_text",
  text = "hello"
}

(The only way to otherwise remove/clean-up the vb:object from the vb.views table is to use vb.views:remove_child(). The above is much simpler, and I'd only use remove_child() if really needed to dynamically remove an object from a visible GUI.)


Edited by joule, 14 December 2016 - 08:49.


#8 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 14 December 2016 - 13:28

Raul, don't ask me to explain it as I don't understand Renoise lua myself.

 

Thanks 4Tey, but the problem was to work the slider also. Actually, your modified xrnx is like a step back, before adding the lines to make the slider work. I have returned to the original xrnx and followed the instructions of joule.

 

Some related advice:

 

I've started using the following approach. I don't know if it is the neatest way of doing things, but these guidelines seem very practical to me:

 

1) Only use an ID if you really have to. E g, when you either have to 1) get/set a vb:object property from some other place, 2) add/remove an object at some later time.

2) Very handy thing to avoid some headache and the common "was already added" error when using ID's.

vb.views.my_text = nil -- practical stuff
vb:text {
  id = "my_text",
  text = "hello"
}

(The only way to otherwise remove/clean-up the vb:object from the vb.views table is to use vb.views:remove_child(). The above is much simpler, and I'd only use remove_child() if really needed to dynamically remove an object from a visible GUI.)

  1. Yes, I always use an unique ID in each element.
  2. I have added the following lines and relocate some functions inside the function global content "gt16_content()". The lines for ID nil:
vb.views.attab_t = nil
vb.views.attab_f = nil
vb.views.pt_index = nil
vb.views.tr_index = nil
vb.views.tr_name = nil
vb.views.tr_color = nil
vb.views.pt_count = nil
vb.views.tr_count = nil
vb.views.bar_count = nil
vb.views.tr_send = nil
vb.views.cbox_bars = nil
vb.views.cbox_bars = nil
vb.views.cbox_bars_f = nil
vb.views.cbox_bars_t = nil
vb.views.navt_undo = nil
vb.views.navt_redo = nil
vb.views.navt_cln = nil
vb.views.navt_del = nil
vb.views.note_on_off = nil
vb.views.collapse_expand_gr = nil
vb.views.navt_swap_left = nil
vb.views.navt_swap_right = nil
vb.views.navt_first = nil
vb.views.navt_02 = nil
vb.views.navt_03 = nil
vb.views.navt_last = nil
vb.views.navs_up = nil
vb.views.navs_down = nil
vb.views.play = nil
vb.views.stop = nil
vb.views.help_switch = nil
vb.views.nav_col = nil
vb.views.nav_track = nil
vb.views.index_value = nil
vb.views.col_value = nil
vb.views.hlptab_cls = nil
vb.views.hlptab_hlp = nil
vb.views.hlptab_abo = nil
vb.views.hlptab_tls = nil
vb.views.tools_switch = nil
vb.views.at_switch = nil

vb.views.tools_switch = nil
vb.views.tlstab_01 = nil
vb.views.tlstab_02 = nil
vb.views.tlstab_03 = nil
vb.views.tlstab_04 = nil
vb.views.tlstab_05 = nil
vb.views.tlstab_06 = nil
vb.views.tlstab_07 = nil
vb.views.tlstab_08 = nil
vb.views.tlstab_09 = nil
vb.views.tlstab_10 = nil

It looks like I'm close to the final solution... ^_^

 

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

 

Now I have a small problem with "require". I use this:

require ("tools/tool_02") --tool_02.lua




--piece of GUI:

vb:column {
  visible = false,
  id = 'tlstab_02',
  style = 'body',
  width = 694,
  margin = 08,
  --- ---
  MESS, --Tool 02 Added!
  --- ---
},

And the tool_02.lua (inside the folder "tools/"):

MESS =

vb:column {
  --- ---
  vb:horizontal_aligner {
    vb:text { text = 'MESS: Matrix Editor Specific Sequencer' }
  }
  --- ---
}

What I want is to split the GUI into parts. When I start Renoise, the require... and "MESS" (the tool_02.lua), charge well. However, when closing GT16-Colors tool and restart, the error appears (caused by the line "MESS, --Tool 02 Added!" ):

*** std::logic_error: 'ViewBuilder: trying to add a view which already was added, probably into a different parent view.'
*** stack traceback:
***   [C]: in function 'column'
***   main.lua:1446: in function 'gt16_content'
***   main.lua:1749: in function <main.lua:1744>
1) I use MESS to link through the require. How do I avoid this error? I have 10 items like this but they will be solved the same.

...

...

 

2) Apart from that, I think it is necessary to adjust the load of the principal tool (GT16-Colors). For some reason the tool loads before Renoise and stays behind the window of Renoise (it does not stay above). It should not even appear...

 

The actual xrnx tool: Attached File  ulneiz.GT16-Colors_v1.1.xrnx   75.36KB   34 downloads (notice to readers: this does not work correctly)

 

Thanks!


:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#9 danoise

danoise

    Probably More God or Borg Than Human Member

  • Renoise Team
  • PipPipPipPipPipPipPipPipPipPipPipPipPipPipPip
  • 6483 posts
  • Gender:Male
  • Location:Berlin
  • Interests:wildlife + urban trekking

Posted 14 December 2016 - 14:23

These errors are thrown because you're building the UI multiple times. Like joule points out, the "id" property can be a bit tricky to deal with. 

I haven't studied the code in detail, but I'm guessing that you don't necessarily need to tear everything down and rebuild it. Building it just once could work?

 

@Raul: remember that I promised to supply some sample code, demonstrating how to build a scrollable table? 

I'm working on that right now, will show several things and probably make you scratch your head once or twice - but among other things, it shows a "best practice" (well, my best practice anyway...) on how to build a dialog that will auto-launch when the Renoise starts. 


Tracking with Stuff. API wishlist | Soundcloud


#10 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 14 December 2016 - 15:02

@Raul: remember that I promised to supply some sample code, demonstrating how to build a scrollable table? 

I'm working on that right now, will show several things and probably make you scratch your head once or twice - but among other things, it shows a "best practice" (well, my best practice anyway...) on how to build a dialog that will auto-launch when the Renoise starts. 

Hi Danoise. Thanks!  ^_^

 

I am able to build this tool with a single file, but it would be very long. I need to split the GUI, and have one main with 10 separate modules. The GUI of this tool is complex. I just need to solve the problem of "require - MESS" to have a example and repeat with the other modules. The question is, how to split the GUI (the ViewBuilder) into multiple files xxx.lua without errors. Maybe I should not use require, and there is another way.

 

PRINCIPAL GUI (GT16-Colors)

   GUI tool module 01

   GUI tool module 02 --> MESS (tool_02.lua), ...the example

   GUI tool module 03

   GUI tool module 04

   GUI tool module 05

   GUI tool module 06

   GUI tool module 07

   GUI tool module 08

   GUI tool module 09

   GUI tool module 10

 

What I do is take a piece of GUI (ViewBuilder) and place it on another .lua archive without errors... This is not possible?  :huh:

 

@Raul: remember that I promised to supply some sample code, demonstrating how to build a scrollable table? 

I'm working on that right now, will show several things and probably make you scratch your head once or twice - but among other things, it shows a "best practice" (well, my best practice anyway...) on how to build a dialog that will auto-launch when the Renoise starts. 

 

Yes, I am pending. I hope it is not too complex to implement. The truth that building some things is a headache.


Edited by Raul (ulneiz), 14 December 2016 - 15:04.

:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#11 4Tey

4Tey

    Chief Above Chief Member

  • Normal Members
  • PipPipPipPipPipPip
  • 388 posts
  • Gender:Male

Posted 14 December 2016 - 15:26

Thanks 4Tey, but the problem was to work the slider also. Actually, your modified xrnx is like a step back, before adding the lines to make the slider work. I have returned to the original xrnx and followed the instructions of joule.

Yes Raul, you took my 'step back' and followed the instructions of Joule.  And did it work?  Nope.  Why?  Why do you think I took a step back?  Do you think it will work when Danoise presents his example scrollable table?  It might.  In the context of Danoise code that is.  You will just blissfully copy Danoise code and try and place it into your GT16 code.  Why do this?  Because it is easy, it is faster.  What's the problem with this approach?  Well over time you get the situation you have now with the current state of your code.  Most of your code Raul is built from copy/pasting, without fully understanding the underlying principles of basic lua programming and how it interacts with Renoise.  It is remarkable it works at all really.

 

You see Raul Danoise and Joule are speaking to you as though you already have a fair experience of programming.  Sure they will show you (advanced) examples and so forth, which you then take and try to apply to your own tool.  But this approach isn't too good in the long run.  You are trying to write too complicated a tool for your own ability.  Danoise and Joule are just giving you examples that are too advanced.  Even I can't understand some of the examples these coders are posting!  (And I think it is safe to say I'm a more advanced programmer than you are Raul.)

 

My best advice:  Only you Raul can learn how to program.  You've got to simplify everything.  Here is my philosophy that I've followed for the past god knows how many years now...

 

Only program what you understand



#12 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 14 December 2016 - 16:30

Thanks 4Tey, but the problem was to work the slider also. Actually, your modified xrnx is like a step back, before adding the lines to make the slider work. I have returned to the original xrnx and followed the instructions of joule.

Yes Raul, you took my 'step back' and followed the instructions of Joule.  And did it work?  Nope.  Why?  Why do you think I took a step back?  Do you think it will work when Danoise presents his example scrollable table?  It might.  In the context of Danoise code that is.  You will just blissfully copy Danoise code and try and place it into your GT16 code.  Why do this?  Because it is easy, it is faster.  What's the problem with this approach?  Well over time you get the situation you have now with the current state of your code.  Most of your code Raul is built from copy/pasting, without fully understanding the underlying principles of basic lua programming and how it interacts with Renoise.  It is remarkable it works at all really.

 

You see Raul Danoise and Joule are speaking to you as though you already have a fair experience of programming.  Sure they will show you (advanced) examples and so forth, which you then take and try to apply to your own tool.  But this approach isn't too good in the long run.  You are trying to write too complicated a tool for your own ability.  Danoise and Joule are just giving you examples that are too advanced.  Even I can't understand some of the examples these coders are posting!  (And I think it is safe to say I'm a more advanced programmer than you are Raul.)

 

My best advice:  Only you Raul can learn how to program.  You've got to simplify everything.  Here is my philosophy that I've followed for the past god knows how many years now...

 

Only program what you understand

 

 

Hi 4Tey. I tell you a little what has happened here. I'm trying to create my own tool (add more things for new version), and learning by the way, also. Almost 90% of the code I need is already built, missing some details, and many have. So far, I understand most of the code, but I had a pending issue: make interacting a slider with Renoise. For this slider to work, my initial code must have a particular order. When you have modified the file, you have just returned to the starting point, "one step back".

 

My tool already worked 100% before (without errors), but without using the slider. The slider did not work and that's why I asked int the forum. I saw a Joule tool, which had something similar (joule.no0b.Transporter.xrnx). I tried to look at the code, and of course, I try to understand it. In it uses several lines = nil, which have to do with the load of viewbuilder, that is a little confusing.

 

Now I'm at a new point. The slider works, and I know why it works. What I want is use various lua archives for different pieces of the GUI (viewbuilder), which is as it was initially, because the tool is long.

 

On the subject of Danoise of scrollable table, it is an issue that has nothing to see here, comes from another topic pending. Nothing to see.

 

Thank you for your advice. Maybe it will surprise you. But I'm learning a lot so far. I assure you that if the tool works, it's because I understand it. If something I do not understand, usually it does not work. If you think the tool is a copy and paste of pieces, you are wrong. If I have specific doubts, I will continue to ask; with a little guidance, I get it working. Have you learned by a example? Sometimes a simple example is enough. For example, comment #7, just what was needed.

 

Just a reflection. Sometimes you can see the code you do not understand. When you verify that it works and you manipulate it, you understand it, and that code has not been programmed by you.

You can try to program things that you initially do not understand. You have learned them on the fly.

 

Thank you for the advice! It is a pleasure to learn from the experts.


:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#13 joule

joule

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1516 posts
  • Gender:Not Telling
  • Location:Sweden
  • Interests:music, philosophy, engineering

Posted 14 December 2016 - 16:49

Yeah, it's good to also provoke oneself by trying out things that you don't understand 100%. Then trial and error, to understand it a bit further. Then make your own implementation to solidify/verify your understanding. That's why I try to help by giving examples rather than "the exact code". (And it's unavoidable that you later discover, looking back at old code, that you didn't quite understand things the way you thought you did.)

 

On this topic.. it would be cool if there was a short video tutorial about Renoise scripting. Some natural steps how a total programming no0b (me) would learn scripting. Chapters could be something like 1) variables, 2) scopes, 3) functions - passing and returning values, 4) tables and iteration, 5) observables, 6) preferences, 7) gui building, 8) classes.


Edited by joule, 14 December 2016 - 16:52.


#14 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 14 December 2016 - 17:37

Yeah, it's good to also provoke oneself by trying out things that you don't understand 100%. Then trial and error, to understand it a bit further. Then make your own implementation to solidify/verify your understanding. That's why I try to help by giving examples rather than "the exact code". (And it's unavoidable that you later discover, looking back at old code, that you didn't quite understand things the way you thought you did.)

 

Exact! Beyond the critics or the method of learning employed of any user, what a novice person values most is that people help him, in any field. Here I believe that two things happen, summarized in one sentence. You can know 100% of the lua code, but if you do not know the Renoise enabled API for scriping, you can not create a tool. This opens a field of lagoons. The example is very basic. Some lines of code required, is not available in the official documentation of Renoise. You can look at the forums or the code of the tools already built to find a solution. There are things that can be built, some not, and this creates doubts even the most expert person. Imagine for a newbie.

 

As long as we help each other, everything will work.

 

On the other hand, a recent reaction that made me laugh in the forum (you know there's a lot of criticism for Renoise's slow development). One user said: "we will learn all the lua code and we will create tools; and all done!" As if it were that easy. People want to write music, not write code. People who write code is by passion rather than by necessity.

 

That's why there are a lot of "tools", which basically are two buttons with two functions. No passion. Is not easy, and you need to invest time, time you do not use to compose music. Then a newbie enters the forum, and wants to create a tool, and even if he has the oficial documentation, he will have a lot of obstacles, Even some internal bug of Renoise, also, in addition to some criticism from other users.

 

On this topic.. it would be cool if there was a short video tutorial about Renoise scripting. Some natural steps how a total programming no0b (me) would learn scripting. Chapters could be something like 1) variables, 2) scopes, 3) functions - passing and returning values, 4) tables and iteration, 5) observables, 6) preferences, 7) gui building, 8) classes.

 

This would be great, but who would? Who gets time for this?


:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#15 danoise

danoise

    Probably More God or Borg Than Human Member

  • Renoise Team
  • PipPipPipPipPipPipPipPipPipPipPipPipPipPipPip
  • 6483 posts
  • Gender:Male
  • Location:Berlin
  • Interests:wildlife + urban trekking

Posted 14 December 2016 - 19:22

To an extent, I can understand 4Tey... there are so many concepts that have taken me years to fully absorb. Patience you must have... 

 

The way I see it, learning programming has two stages: first of all, learning to express ideas in code (in this case, lua). And secondly, learning how to apply it to something concrete - such as the Renoise API. In many ways, the first step is the most exciting as it enables you to think in code. The second is mostly a question of memorizing things. But essential of course, once you need to actually get something done. 

 

But of course, you don't _have_ to learn things in any specific order. It just helps to get some of the fundamentals right to begin with.   

 

And while it would be nice with some learning resources, the idea of a Renoise scripting tutorial that cover "everything"´ - I think that's too much ground to cover. I would simply focus on what makes the Renoise API different from vanilla lua. This would be cool, because you could learn to express yourself using the multitude of great tutorials that are already out there. And, once things start to settle, go deeper by studying each of the things that are specific to Renoise. 

 

there are a lot of "tools", which basically are two buttons with two functions

 

Well, some of my most essential tools have no buttons at all. Just a single keyboard shortcut or a menu entry.

Surely the quality of a tool is measured on it's usefulness and not how many buttons it has  ^_^

 

But as the complexity of a tool grows, some of the things I mentioned (language fundamentals) becomes essential. Basically, you need to refactor, restructure things. And this is hard if things are assembled ad-hoc. Just maybe, the example I'm about to provide will be of use - it's using classes, which I find are uniquely suited for building a large (and re-usable) codebase.


Tracking with Stuff. API wishlist | Soundcloud


#16 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 15 December 2016 - 13:45

To an extent, I can understand 4Tey... there are so many concepts that have taken me years to fully absorb. Patience you must have... 

 

I get the feeling that 4Tey bothered a bit, because I did not explain myself well. I had my code at a point A. I asked to change something. I modified the code at a point B, following the signs of joule (I knew I was close to the solution). But something was still missing. I shared the tool at point B. 4Tey took the tool and modified it, returning to point A by chance. Then I told him that he had "stepped back".  :unsure:

 

My English is not perfect, and these are forums, all written. Any nonsense can be interpreted wrongly. Sometimes it is enough to put an icon for someone to take offense.

 

Well, some of my most essential tools have no buttons at all. Just a single keyboard shortcut or a menu entry.

Surely the quality of a tool is measured on it's usefulness and not how many buttons it has

I mean there are many tools of this style, simple, without the need to invest much time in building them, because people are not for these things. You are just the opposite example  ^_^

 

But as the complexity of a tool grows, some of the things I mentioned (language fundamentals) becomes essential. Basically, you need to refactor, restructure things...

Right now I'm at this point, reshaping the code. But not by doing things wrong. I know I'm on the right track with the code, because it works perfectly when I fix it.

 

Following with the thread, I will put a history of what happens to me:

  1. Point A. Code with GUI (Viewbuilder) chopped in 11 parts, 1 main and 10 pieces (for tool modules) using "require" (10 requires). I want this structure to be able to manipulate the "tool modules" independently.
  2. Point B. Insert a "slider" (or several) that interacts correctly with Renoise. This step, forces me to include the GUI inside a function, destroying any viewbuilder invoked from require.
  3. Point C (actually). Problem to keep chunks of the GUI out of the function that wraps the main GUI, because Renoise interprets that there are several viewbuilders, the error (ViewBuilder: trying to add a view which already was added, probably into a different parent view).

A scheme:

Main.lua --->> general function that wraps the "main GUI" (apparently it is necessary to make the slider) --->> various requires (tools/tool_xx.lua) with "children GUI" (pieces of the GUI):

scheme-pieces-gui-errors.png

Is it necessary for the entire GUI to be within the "general function"? Is there a way to separate it?

 

EDIT: Forget this!!!  I have decided to do without the slider. Because of the slider, I can not split the GUI and because of this, each tool module does not preserve the window settings when opening and closing the main tool while it is being used when composing. I can build another window with sliders. But I wanted a single window for everything. It's too complicated. In this way, I can keep the above scheme (without the general function).


Edited by Raul (ulneiz), 16 December 2016 - 00:33.

:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#17 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 15 December 2016 - 23:23

EDIT: I am trying to include the Autostart checkbox in my tool. The checkbox is already working (saved correctly in the file preferences.xml). But I encounter this problem...

 

And other problem: http://forum.renoise...-after-startup/

 

:ph34r:

 

This was bothering me for days. I thought it was due to something wrong in my code. but it's not like that.

 

I have taken the Danoise demo tool as an example to apply a solution. 

 

Use the line: "waiting_to_show_dialog = prefs.autostart.value" Following the clue in the code I come to this:

if self.waiting_to_show_dialog then
  renoise.tool().app_idle_observable:add_notifier(self,self.idle_notifier_waiting)
end

The code use "renoise.tool().app_idle_observable" to force the dialog up the window of Renoise.

 

I use the code to show the dialog:

--------------------------------------------------------------------------------
-- SHOW_DIALOG
--------------------------------------------------------------------------------
function show_dialog()
  if dialog and dialog.visible then dialog:show() return end -- Prevents duplicate of the floating window ( when reaching this, the dialog never was created before or was closed ) 
  dialog = renoise.app():show_custom_dialog ( ' '.. tool_name .. '  v' .. tool_version .. '     by: ulneiz' , content , keyhandler )
  renoise.app():show_status('GT16-Colors is charged... Enjoy the colors and control!')
end
renoise.tool().app_idle_observable:add_notifier(??????????????) <-------Is it possible to place the code (a function?) here to overlap the window of tool?

--autostart
renoise.tool().app_new_document_observable:add_notifier( function()
  if prefs.autostart.value then
    show_dialog()
  end   
end )

Renoise.tool (). App_idle_observable: add_notifier (??????????????)

 

Is there any simple function to achieve this?


Edited by Raul (ulneiz), 16 December 2016 - 00:15.

:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#18 danoise

danoise

    Probably More God or Borg Than Human Member

  • Renoise Team
  • PipPipPipPipPipPipPipPipPipPipPipPipPipPipPip
  • 6483 posts
  • Gender:Male
  • Location:Berlin
  • Interests:wildlife + urban trekking

Posted 16 December 2016 - 02:06

Yes, the tool example I provided is working because I've gone ahead and implemented the workaround in the vDialog itself. 
(achieved by passing "waiting_to_show_dialog = true" when creating the instance) 
 
For your code to work you will need to implement this yourself
 

1. Make sure your tool receives notifications from app_idle_observable
2. Add some variable that is set to true when your tool _was supposed_ to display it's UI
3. Inside the idle loop, check if you tool was supposed to show - if so, show it and then reset the variable
(or detach the idle loop completely if you're really concerned about wasting CPU cycles).


Let's just (because we're sort-of dealing with vLib anyway) see how it's done there:

The vDialog constructor has the following statement

  if self.waiting_to_show_dialog then
    renoise.tool().app_idle_observable:add_notifier(self,self.idle_notifier_waiting)
  end

And the idle notifications are handled by this method:
 

function vDialog:idle_notifier_waiting()
  TRACE("vDialog:idle_notifier_waiting()")

  local idle_obs = renoise.tool().app_idle_observable
  local remove_notifier = false

  if self.waiting_to_show_dialog 
    and renoise.song() 
  then
    self.waiting_to_show_dialog = false
    self:show() 
    remove_notifier = true
  end  

  if remove_notifier and idle_obs:has_notifier(self,vDialog.idle_notifier_waiting) then
    idle_obs:remove_notifier(self,vDialog.idle_notifier_waiting)
  end

end

You don't have to use classes, of course. 

 

The "vDialog:" in front is what associates a function with a class. It's then called a "method"
Same with "waiting_to_show_dialog", the "self." in front tells lua it is a property of the class. 
 

Remove all references to "self"

 
if waiting_to_show_dialog then
  renoise.tool().app_idle_observable:add_notifier(idle_notifier_waiting)
end
 

And declare the function like this instead: 

 

function idle_notifier_waiting()

...etc

 

 

The important thing is how it's all connected together

  • When creating a vDialog (constructor), and "waiting_to_show_dialog" is true, a method is attached to the idle observer
  • Now... the method is continously polling, waiting for renoise.song() to become available. This usually happens right away, within a few milliseconds. 
  • Once the song is there, the dialog will be shown. Immediately, the notifier is removed, freeing a (tiny bit) of CPU 

Tracking with Stuff. API wishlist | Soundcloud


#19 4Tey

4Tey

    Chief Above Chief Member

  • Normal Members
  • PipPipPipPipPipPip
  • 388 posts
  • Gender:Male

Posted 16 December 2016 - 05:45

EDIT: Forget this!!!  I have decided to do without the slider. Because of the slider, I can not split the GUI and because of this, each tool module does not preserve the window settings when opening and closing the main tool while it is being used when composing. I can build another window with sliders. But I wanted a single window for everything. It's too complicated. In this way, I can keep the above scheme (without the general function).

So you have gone back to having your 'content' gui build table fully global (rather than defined in the function gt16_content), along with the viewbulder vb like how you originally programmed the tool?  You see Raul if you take a moment to think about this you can't have 'content' gui table local in a function with your vb object global.  You'll get an error (as you have found) when the user opens your tool subsequent times.  As now 'content' is trying to always initialise the global vb when it executes the gt16_content function.  If you move your vb into a function (and reduce scope) you would have to completely readjust most of your code that uses vb (which is a lot and hence why I didn't do it in my example) and start passing vb around as a parameter in functions that require vb (which to be honest is how I would write it.) 

 

Why do you want your tool to autostart anyway?  Surely it would be fine just bound to a key shortcut?  Save you the hassle with notifiers :)

 

Also two other things I noticed Raul:

Your 'includes':

require ("tools/tool_01")

would probably need to be changed to:

require ("Tools/tool_01")

for it to work under Linux (and I suspect MacOS) as they are case sensitive operating systems.

 

And don't forget about the key handler function, this:

function keyhandler( key )

would need to be changed to something like:

function keyhandler( dlg,key )

otherwise your key handler will throw errors.



#20 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 16 December 2016 - 08:21

 

Yes, the tool example I provided is working because I've gone ahead and implemented the workaround in the vDialog itself. 
(achieved by passing "waiting_to_show_dialog = true" when creating the instance) 
 
For your code to work you will need to implement this yourself
 


Let's just (because we're sort-of dealing with vLib anyway) see how it's done there:

The vDialog constructor has the following statement

  if self.waiting_to_show_dialog then
    renoise.tool().app_idle_observable:add_notifier(self,self.idle_notifier_waiting)
  end

And the idle notifications are handled by this method:
 

function vDialog:idle_notifier_waiting()
  TRACE("vDialog:idle_notifier_waiting()")

  local idle_obs = renoise.tool().app_idle_observable
  local remove_notifier = false

  if self.waiting_to_show_dialog 
    and renoise.song() 
  then
    self.waiting_to_show_dialog = false
    self:show() 
    remove_notifier = true
  end  

  if remove_notifier and idle_obs:has_notifier(self,vDialog.idle_notifier_waiting) then
    idle_obs:remove_notifier(self,vDialog.idle_notifier_waiting)
  end

end

You don't have to use classes, of course. 

 

The "vDialog:" in front is what associates a function with a class. It's then called a "method"
Same with "waiting_to_show_dialog", the "self." in front tells lua it is a property of the class. 
 

Remove all references to "self"

 
if waiting_to_show_dialog then
  renoise.tool().app_idle_observable:add_notifier(idle_notifier_waiting)
end
 

And declare the function like this instead: 

 

function idle_notifier_waiting()

...etc

 

 

The important thing is how it's all connected together

  • When creating a vDialog (constructor), and "waiting_to_show_dialog" is true, a method is attached to the idle observer
  • Now... the method is continously polling, waiting for renoise.song() to become available. This usually happens right away, within a few milliseconds. 
  • Once the song is there, the dialog will be shown. Immediately, the notifier is removed, freeing a (tiny bit) of CPU 

 

 

Thanks! Work 90% with this:

--------------------------------------------------------------------------------
-- SHOW_DIALOG
--------------------------------------------------------------------------------
function show_dialog()
  if dialog and dialog.visible then dialog:show() return end -- Prevents duplicate of the floating window ( when reaching this, the dialog never was created before or was closed ) 
  dialog = renoise.app():show_custom_dialog ( ' '.. tool_name .. '  v' .. tool_version .. '     by: ulneiz' , content , keyhandler )
  renoise.app():show_status('GT16-Colors is charged... Enjoy the colors and control!')
end

waiting_to_show_dialog = prefs.autostart.value

function idle_notifier_waiting()
  local idle_obs = renoise.tool().app_idle_observable
  local remove_notifier = false

  if waiting_to_show_dialog 
    and renoise.song() 
  then
    waiting_to_show_dialog = false
    show_dialog() 
    remove_notifier = true
  end  

  if remove_notifier and idle_obs:has_notifier( idle_notifier_waiting ) then
    idle_obs:remove_notifier( idle_notifier_waiting )
  end
end

if waiting_to_show_dialog then
  renoise.tool().app_idle_observable:add_notifier(idle_notifier_waiting)
end

renoise.tool().app_new_document_observable:add_notifier (
  function()
    if prefs.autostart.value then
      show_dialog()
    end   
  end
)

The tool window now appears correctly above Renoise. However, something seems to be missing. When you click on the Renoise window, the renoise window superimposes the tool window. I close and re-open the tool and this no longer happens, the tool always remains above. It seems that something is missing to force the window to always be superimposed...  ^_^

 

What is missing? 

 

If this works at 100%, I will create a new thread explaining all the steps, so that it is available in a clear way... Danoise, I've only seen your tools with this feature. It seems that nobody knows how to build it


Edited by Raul (ulneiz), 16 December 2016 - 08:23.

:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#21 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 16 December 2016 - 08:37

So you have gone back to having your 'content' gui build table fully global (rather than defined in the function gt16_content), along with the viewbulder vb like how you originally programmed the tool?  You see Raul if you take a moment to think about this you can't have 'content' gui table local in a function with your vb object global.  You'll get an error (as you have found) when the user opens your tool subsequent times.  As now 'content' is trying to always initialise the global vb when it executes the gt16_content function.  If you move your vb into a function (and reduce scope) you would have to completely readjust most of your code that uses vb (which is a lot and hence why I didn't do it in my example) and start passing vb around as a parameter in functions that require vb (which to be honest is how I would write it.) 

 

Why do you want your tool to autostart anyway?  Surely it would be fine just bound to a key shortcut?  Save you the hassle with notifiers :)

 

Also two other things I noticed Raul:

Your 'includes':

require ("tools/tool_01")

would probably need to be changed to:

require ("Tools/tool_01")

for it to work under Linux (and I suspect MacOS) as they are case sensitive operating systems.

 

And don't forget about the key handler function, this:

function keyhandler( key )

would need to be changed to something like:

function keyhandler( dlg,key )

otherwise your key handler will throw errors.

 

Thanks for the advice! I will use all folders in lowercase. I quit "dialog" or "dlg", to disable the keys because it was changing the modules of site.

 

There really is no problem for using the GUI in a single file. The problem is not to modify the code or change things, the problem is me, that I want to have separate modules. Using sliders (or similar elements) does not allow you to split the GUI into different files. For them, is necessary to invoke a new window.

 

The tool already has 90% of autostart (missing a detail) and can already be invoked by keyboard command.

 

4Tey, are you building any tool now?


Edited by Raul (ulneiz), 16 December 2016 - 08:38.

:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#22 4Tey

4Tey

    Chief Above Chief Member

  • Normal Members
  • PipPipPipPipPipPip
  • 388 posts
  • Gender:Male

Posted 16 December 2016 - 20:52

Am I building a tool?  Nah, I find that joule/danoise/ledger/afta8 have pretty much covered all the bases Raul ;)

 

Just as a side thought: With all the talent of people mentioned above I often wonder why joule/danoise/afta8 etc don't collaborate on a tool.  I often imagine that collaborative coding is a skill in itself to coordinate :)



#23 Raul (ulneiz)

Raul (ulneiz)

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1078 posts
  • Gender:Male
  • Location:Spain

Posted 16 December 2016 - 21:13

Just as a side thought: With all the talent of people mentioned above I often wonder why joule/danoise/afta8 etc don't collaborate on a tool.  I often imagine that collaborative coding is a skill in itself to coordinate :)

 

True. I complained about this on one occasion. When I seriously started building my first serious tool, I wondered why there are no collaborations between different experts. Each one goes by one side.

 

I also wonder why Danoise has no responsibility within Renoise's team to improve it. He could be adding things and even fixing bugs, while taktik is in another isolated world. He seems to make only tools, while Renoise is stuck. And being honest, the renoise forum is maintained because it's Danoise. But this would be a disaster.

 

Joule is also making its own tool "Chord tracker". I guess he works at short times, slowly, in installments. I do not see much afta8 activity lately. Certainly very few people are trying to do things with Lua API. 

 

It is also true that it is complicated to collaborate. Each person is from a different country. On the other hand, how is it possible that there is no serious news of Taktik? Does no nobody know him closely and can filter something? It is a very antisocial philosophy. What problem is there in informing the forums?

 

One day he answered a user by telling him that he was working on something that had nothing to do with Renoise / Redux, and the people applauded him. The world upside down.

 

Anyway, LUA is fun.  :clownstep:


  • ffx likes this

:excl: Development of my tool: GT16-Colors

 

:excl: My API wishlist R3.1 (updated 24 July 2017):

Spoiler

 

:excl: My Renoise 3.1 wishlist (updated 26 September 2017):

Spoiler

#24 joule

joule

    Guruh Motha Fakka is Levitating and Knows Everything About Renoise Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPip
  • 1516 posts
  • Gender:Not Telling
  • Location:Sweden
  • Interests:music, philosophy, engineering

Posted 17 December 2016 - 11:17

Is there any particular idea that would benefit from a team of scripters atm?



#25 ffx

ffx

    Composes without Wires burns Directly from Brain to DVD that is already in Store Member

  • Normal Members
  • PipPipPipPipPipPipPipPipPipPipPipPipPipPip
  • 3022 posts
  • Gender:Not Telling
  • Interests:macOS fanboying

Posted 17 December 2016 - 11:22

Renoise 4.0


MacOS 10.12.6 Retina, Renoise 3.1 64 bit   -   Tuned Shortcuts | Multi-Jump From/To Send | Quick Template | Insert Native DSP Menu (incl. deprecated)
HM Soundcloud | HM Bandcamp





Also tagged with one or more of these keywords: slider tracks, notifier, _observable