{
   "author" : "Dindoléon",
   "name" : "MultiButtonView",
   "description" : "Demo of a custom modular menu, allowing you to switch between several buttons instances. Connect to a function using view.bindFunction( function ). Supports layout integration, colors customisation, orientation, states and current state reassignment.",
   "ancestor_list" : [],
   "labels" : [
      "gui",
      "tool",
      "interface",
      "menu"
   ],
   "id" : "1-5fe",
   "is_private" : null,
   "code" : "(\r\n\r\nvar win = Window( \"MultiButtonView\", Rect( 0, 300, 600, 150 ) );\r\n\r\nvar multi_button_view;\r\n\r\n// State = [ \"Displayed Text\", Text Color if selected, Border Color if selected, BG Color if selected,\r\n// Text Color if unselected, Border Color if unselected, BG Color if unselected ]\r\nvar multi_button_view_states = [\r\n\t[ \"Option 1\", Color.black, Color.red, Color.red, Color.white, Color.white, Color.black ],\r\n\t[ \"Option 2\", Color.black, Color.red, Color.green, Color.white, Color.white, Color( 0.2, 0.2, 0.2 ) ],\r\n\t[ \"Option 3\", Color.black, Color.red, Color.blue, Color.white, Color.white, Color.black ]\r\n].asList;\r\n\r\nvar demo_view = UserView();\r\nvar demo_string = \"\";\r\nvar demo_function;\r\n\r\n~get_multi_button_view = { | states_array, default_state, orientation, font, has_inside_margin |\r\n\r\n\tvar multi_button_view = UserView();\r\n\tvar margin_min_max = Point( 2, 10 );\r\n\tvar current_state = default_state;\r\n\tvar states = states_array;\r\n\tvar states_number = states.size;\r\n\tvar binded_function = nil;\r\n\r\n\tmulti_button_view.background_( Color.black );\r\n\tmulti_button_view.drawFunc = { | view |\r\n\r\n\t\tvar margin;\r\n\t\tvar color_border_ratio = 0.5;\r\n\t\tvar button_size;\r\n\r\n\t\tif( orientation == \\horizontal, {\r\n\t\t\tmargin = view.bounds.height * 0.02;\r\n\t\t}, {\r\n\t\t\tmargin = view.bounds.width * 0.02;\r\n\t\t} );\r\n\r\n\t\tif( margin < margin_min_max.x, { margin = margin_min_max.x } );\r\n\t\tif( margin > margin_min_max.y, { margin = margin_min_max.y } );\r\n\r\n\t\tif( orientation == \\horizontal, {\r\n\t\t\tbutton_size = view.bounds.width - ( margin * 2 ) - ( margin * ( states.size - 1 ) ) / states.size;\r\n\t\t}, {\r\n\t\t\tbutton_size = view.bounds.height - ( margin * 2 ) - ( margin * ( states.size - 1 ) ) / states.size;\r\n\t\t} );\r\n\r\n\t\tstates.do( { | state, index |\r\n\r\n\t\t\tvar string_rect;\r\n\t\t\tvar string_color;\r\n\r\n\t\t\tif( current_state == index, {\r\n\t\t\t\tPen.fillColor_( state[ 2 ] )\r\n\t\t\t}, {\r\n\t\t\t\tPen.fillColor_( state[ 5 ] )\r\n\t\t\t} );\r\n\r\n\t\t\tif( orientation == \\horizontal, {\r\n\t\t\t\tPen.fillRect(\r\n\t\t\t\t\tRect(\r\n\t\t\t\t\t\tmargin + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\t\tmargin,\r\n\t\t\t\t\t\tbutton_size,\r\n\t\t\t\t\t\tview.bounds.height - ( margin * 2 )\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}, {\r\n\t\t\t\tPen.fillRect(\r\n\t\t\t\t\tRect(\r\n\t\t\t\t\t\tmargin,\r\n\t\t\t\t\t\tmargin + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\t\tview.bounds.width - ( margin * 2 ),\r\n\t\t\t\t\t\tbutton_size\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t} ); // End of first border\r\n\r\n\t\t\tif( current_state == index, {\r\n\t\t\t\tPen.fillColor_(\r\n\t\t\t\t\tColor(\r\n\t\t\t\t\t\tstate[ 2 ].red * color_border_ratio,\r\n\t\t\t\t\t\tstate[ 2 ].green * color_border_ratio,\r\n\t\t\t\t\t\tstate[ 2 ].blue * color_border_ratio,\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}, {\r\n\t\t\t\tPen.fillColor_(\r\n\t\t\t\t\tColor(\r\n\t\t\t\t\t\tstate[ 5 ].red * color_border_ratio,\r\n\t\t\t\t\t\tstate[ 5 ].green * color_border_ratio,\r\n\t\t\t\t\t\tstate[ 5 ].blue * color_border_ratio,\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t} );\r\n\r\n\t\t\tif( orientation == \\horizontal, {\r\n\t\t\t\tPen.fillRect(\r\n\t\t\t\t\tRect(\r\n\t\t\t\t\t\tmargin * 1.5 + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\t\tmargin * 1.5,\r\n\t\t\t\t\t\tbutton_size - margin,\r\n\t\t\t\t\t\tview.bounds.height - ( margin * 3 )\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}, {\r\n\t\t\t\tPen.fillRect(\r\n\t\t\t\t\tRect(\r\n\t\t\t\t\t\tmargin * 1.5,\r\n\t\t\t\t\t\tmargin * 1.5 + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\t\tview.bounds.width - ( margin * 3 ),\r\n\t\t\t\t\t\tbutton_size - margin\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t} ); // End of second border\r\n\r\n\t\t\tif( has_inside_margin, {\r\n\t\t\t\tPen.fillColor_( Color.black )\r\n\t\t\t}, {\r\n\t\t\t\tif( current_state == index, {\r\n\t\t\t\t\tPen.fillColor_( state[ 3 ] )\r\n\t\t\t\t}, {\r\n\t\t\t\t\tPen.fillColor_( state[ 6 ] )\r\n\t\t\t\t} );\r\n\t\t\t} );\r\n\r\n\t\t\tif( orientation == \\horizontal, {\r\n\t\t\t\tPen.fillRect(\r\n\t\t\t\t\tRect(\r\n\t\t\t\t\t\tmargin * 2 + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\t\tmargin * 2,\r\n\t\t\t\t\t\tbutton_size - ( margin * 2 ),\r\n\t\t\t\t\t\tview.bounds.height - ( margin * 4 )\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t}, {\r\n\t\t\t\tPen.fillRect(\r\n\t\t\t\t\tRect(\r\n\t\t\t\t\t\tmargin * 2,\r\n\t\t\t\t\t\tmargin * 2 + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\t\tview.bounds.width - ( margin * 4 ),\r\n\t\t\t\t\t\tbutton_size - ( margin * 2 )\r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t} ); // End of third fill\r\n\r\n\t\t\tif( has_inside_margin, {\r\n\t\t\t\tif( current_state == index, {\r\n\t\t\t\t\tPen.fillColor_( state[ 3 ] )\r\n\t\t\t\t}, {\r\n\t\t\t\t\tPen.fillColor_(\tstate[ 6 ] )\r\n\t\t\t\t} );\r\n\r\n\t\t\t\tif( orientation == \\horizontal, {\r\n\t\t\t\t\tPen.fillRect(\r\n\t\t\t\t\t\tRect(\r\n\t\t\t\t\t\t\tmargin * 3 + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\t\t\tmargin * 3,\r\n\t\t\t\t\t\t\tbutton_size - ( margin * 4 ),\r\n\t\t\t\t\t\t\tview.bounds.height - ( margin * 6 )\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t)\r\n\t\t\t\t}, {\r\n\t\t\t\t\tPen.fillRect(\r\n\t\t\t\t\t\tRect(\r\n\t\t\t\t\t\t\tmargin * 3,\r\n\t\t\t\t\t\t\tmargin * 3 + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\t\t\tview.bounds.width - ( margin * 6 ),\r\n\t\t\t\t\t\t\tbutton_size - ( margin * 4 )\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t)\r\n\t\t\t\t} );\r\n\t\t\t} ); // End of background if has_inside_margin\r\n\r\n\t\t\tif( orientation == \\horizontal, {\r\n\t\t\t\tstring_rect = Rect(\r\n\t\t\t\t\tmargin + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\tmargin,\r\n\t\t\t\t\tbutton_size,\r\n\t\t\t\t\tview.bounds.height - ( margin * 2 )\r\n\t\t\t\t)\r\n\t\t\t}, {\r\n\t\t\t\tstring_rect = Rect(\r\n\t\t\t\t\tmargin,\r\n\t\t\t\t\tmargin + ( index * ( button_size + margin ) ),\r\n\t\t\t\t\tview.bounds.width - ( margin * 2 ),\r\n\t\t\t\t\tbutton_size\r\n\t\t\t\t)\r\n\t\t\t} );\r\n\r\n\t\t\tif( current_state == index, {\r\n\t\t\t\tstring_color = state[1]\r\n\t\t\t}, {\r\n\t\t\t\tstring_color = state[4]\r\n\t\t\t} );\r\n\r\n\t\t\tPen.stringCenteredIn(\r\n\t\t\t\tstate[0],\r\n\t\t\t\tstring_rect,\r\n\t\t\t\tfont,\r\n\t\t\t\tstring_color\r\n\t\t\t);\r\n\t\t} );\r\n\t}; // End of drawFunc\r\n\r\n\tmulti_button_view.mouseDownAction = { | view, x, y |\r\n\r\n\t\tvar new_state;\r\n\t\tif( orientation == \\horizontal, {\r\n\t\t\tnew_state = x.linlin( 0, view.bounds.width, 0, states_number ).asInt;\r\n\t\t}, {\r\n\t\t\tnew_state = y.linlin( 0, view.bounds.height, 0, states_number ).asInt;\r\n\t\t} );\r\n\t\tif( new_state != current_state, {\r\n\t\t\tcurrent_state = new_state;\r\n\t\t\tif( binded_function != nil, { binded_function.value( current_state ) } );\r\n\t\t\tview.refresh;\r\n\t\t} );\r\n\t};\r\n\r\n\t// Custom methods :\r\n\tmulti_button_view.addUniqueMethod( \\bindFunction, { | object, function |\r\n\t\tbinded_function = function\r\n\t} );\r\n\r\n\tmulti_button_view.addUniqueMethod( \\updateStates, { | object, new_states_array |\r\n\t\tstates = new_states_array;\r\n\t\tstates_number = states.size;\r\n\t\tif( current_state > states_number, { current_state = 0 } );\r\n\t\tmulti_button_view.refresh;\r\n\t} );\r\n\r\n\tmulti_button_view.addUniqueMethod( \\setState, { | object, new_state |\r\n\t\tif( ( ( new_state >= 0 ) && ( new_state <= states_number ) ), {\r\n\t\t\tcurrent_state = new_state;\r\n\t\t\tmulti_button_view.refresh;\r\n\t\t} );\r\n\t} );\r\n\r\n\tmulti_button_view.addUniqueMethod( \\valueState, { | object, new_state |\r\n\t\tif( ( ( new_state >= 0 ) && ( new_state <= states_number ) ), {\r\n\t\t\tcurrent_state = new_state;\r\n\t\t\tif( binded_function != nil, { binded_function.value( current_state ) } );\r\n\t\t\tmulti_button_view.refresh;\r\n\t\t} );\r\n\t} );\r\n\r\n\tmulti_button_view\r\n};\r\n\r\nmulti_button_view = ~get_multi_button_view.value( multi_button_view_states, 0, \\horizontal, Font.default, true );\r\nmulti_button_view_states.add( [ \"Option 4\", Color.black, Color.red, Color.cyan, Color.white, Color.white, Color( 0.2, 0.2, 0.2 ) ] );\r\nmulti_button_view.updateStates( multi_button_view_states );\r\n\r\ndemo_view.drawFunc = { | view |\r\n\tPen.stringCenteredIn(\r\n\t\tdemo_string,\r\n\t\tRect(\r\n\t\t\t0,\r\n\t\t\t0,\r\n\t\t\tview.bounds.width,\r\n\t\t\tview.bounds.height\r\n\t\t)\r\n\t)\r\n};\r\n\r\ndemo_function = { | index |\r\n\tdemo_string = \"You picked \" ++ multi_button_view_states[ index ][ 0 ];\r\n\tdemo_view.refresh\r\n};\r\n\r\nmulti_button_view.bindFunction( demo_function );\r\nmulti_button_view.valueState( 2 );\r\n\r\nwin.layout_(\r\n\tVLayout(\r\n\t\t[ multi_button_view, stretch: 2 ],\r\n\t\t[ demo_view, stretch: 1 ]\r\n\t)\r\n);\r\nwin.front;\r\n\r\n)"
}
