// title: MultiButtonView // author: Dindoléon // 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. // code: ( var win = Window( "MultiButtonView", Rect( 0, 300, 600, 150 ) ); var multi_button_view; // State = [ "Displayed Text", Text Color if selected, Border Color if selected, BG Color if selected, // Text Color if unselected, Border Color if unselected, BG Color if unselected ] var multi_button_view_states = [ [ "Option 1", Color.black, Color.red, Color.red, Color.white, Color.white, Color.black ], [ "Option 2", Color.black, Color.red, Color.green, Color.white, Color.white, Color( 0.2, 0.2, 0.2 ) ], [ "Option 3", Color.black, Color.red, Color.blue, Color.white, Color.white, Color.black ] ].asList; var demo_view = UserView(); var demo_string = ""; var demo_function; ~get_multi_button_view = { | states_array, default_state, orientation, font, has_inside_margin | var multi_button_view = UserView(); var margin_min_max = Point( 2, 10 ); var current_state = default_state; var states = states_array; var states_number = states.size; var binded_function = nil; multi_button_view.background_( Color.black ); multi_button_view.drawFunc = { | view | var margin; var color_border_ratio = 0.5; var button_size; if( orientation == \horizontal, { margin = view.bounds.height * 0.02; }, { margin = view.bounds.width * 0.02; } ); if( margin < margin_min_max.x, { margin = margin_min_max.x } ); if( margin > margin_min_max.y, { margin = margin_min_max.y } ); if( orientation == \horizontal, { button_size = view.bounds.width - ( margin * 2 ) - ( margin * ( states.size - 1 ) ) / states.size; }, { button_size = view.bounds.height - ( margin * 2 ) - ( margin * ( states.size - 1 ) ) / states.size; } ); states.do( { | state, index | var string_rect; var string_color; if( current_state == index, { Pen.fillColor_( state[ 2 ] ) }, { Pen.fillColor_( state[ 5 ] ) } ); if( orientation == \horizontal, { Pen.fillRect( Rect( margin + ( index * ( button_size + margin ) ), margin, button_size, view.bounds.height - ( margin * 2 ) ) ) }, { Pen.fillRect( Rect( margin, margin + ( index * ( button_size + margin ) ), view.bounds.width - ( margin * 2 ), button_size ) ) } ); // End of first border if( current_state == index, { Pen.fillColor_( Color( state[ 2 ].red * color_border_ratio, state[ 2 ].green * color_border_ratio, state[ 2 ].blue * color_border_ratio, ) ) }, { Pen.fillColor_( Color( state[ 5 ].red * color_border_ratio, state[ 5 ].green * color_border_ratio, state[ 5 ].blue * color_border_ratio, ) ) } ); if( orientation == \horizontal, { Pen.fillRect( Rect( margin * 1.5 + ( index * ( button_size + margin ) ), margin * 1.5, button_size - margin, view.bounds.height - ( margin * 3 ) ) ) }, { Pen.fillRect( Rect( margin * 1.5, margin * 1.5 + ( index * ( button_size + margin ) ), view.bounds.width - ( margin * 3 ), button_size - margin ) ) } ); // End of second border if( has_inside_margin, { Pen.fillColor_( Color.black ) }, { if( current_state == index, { Pen.fillColor_( state[ 3 ] ) }, { Pen.fillColor_( state[ 6 ] ) } ); } ); if( orientation == \horizontal, { Pen.fillRect( Rect( margin * 2 + ( index * ( button_size + margin ) ), margin * 2, button_size - ( margin * 2 ), view.bounds.height - ( margin * 4 ) ) ) }, { Pen.fillRect( Rect( margin * 2, margin * 2 + ( index * ( button_size + margin ) ), view.bounds.width - ( margin * 4 ), button_size - ( margin * 2 ) ) ) } ); // End of third fill if( has_inside_margin, { if( current_state == index, { Pen.fillColor_( state[ 3 ] ) }, { Pen.fillColor_( state[ 6 ] ) } ); if( orientation == \horizontal, { Pen.fillRect( Rect( margin * 3 + ( index * ( button_size + margin ) ), margin * 3, button_size - ( margin * 4 ), view.bounds.height - ( margin * 6 ) ) ) }, { Pen.fillRect( Rect( margin * 3, margin * 3 + ( index * ( button_size + margin ) ), view.bounds.width - ( margin * 6 ), button_size - ( margin * 4 ) ) ) } ); } ); // End of background if has_inside_margin if( orientation == \horizontal, { string_rect = Rect( margin + ( index * ( button_size + margin ) ), margin, button_size, view.bounds.height - ( margin * 2 ) ) }, { string_rect = Rect( margin, margin + ( index * ( button_size + margin ) ), view.bounds.width - ( margin * 2 ), button_size ) } ); if( current_state == index, { string_color = state[1] }, { string_color = state[4] } ); Pen.stringCenteredIn( state[0], string_rect, font, string_color ); } ); }; // End of drawFunc multi_button_view.mouseDownAction = { | view, x, y | var new_state; if( orientation == \horizontal, { new_state = x.linlin( 0, view.bounds.width, 0, states_number ).asInt; }, { new_state = y.linlin( 0, view.bounds.height, 0, states_number ).asInt; } ); if( new_state != current_state, { current_state = new_state; if( binded_function != nil, { binded_function.value( current_state ) } ); view.refresh; } ); }; // Custom methods : multi_button_view.addUniqueMethod( \bindFunction, { | object, function | binded_function = function } ); multi_button_view.addUniqueMethod( \updateStates, { | object, new_states_array | states = new_states_array; states_number = states.size; if( current_state > states_number, { current_state = 0 } ); multi_button_view.refresh; } ); multi_button_view.addUniqueMethod( \setState, { | object, new_state | if( ( ( new_state >= 0 ) && ( new_state <= states_number ) ), { current_state = new_state; multi_button_view.refresh; } ); } ); multi_button_view.addUniqueMethod( \valueState, { | object, new_state | if( ( ( new_state >= 0 ) && ( new_state <= states_number ) ), { current_state = new_state; if( binded_function != nil, { binded_function.value( current_state ) } ); multi_button_view.refresh; } ); } ); multi_button_view }; multi_button_view = ~get_multi_button_view.value( multi_button_view_states, 0, \horizontal, Font.default, true ); multi_button_view_states.add( [ "Option 4", Color.black, Color.red, Color.cyan, Color.white, Color.white, Color( 0.2, 0.2, 0.2 ) ] ); multi_button_view.updateStates( multi_button_view_states ); demo_view.drawFunc = { | view | Pen.stringCenteredIn( demo_string, Rect( 0, 0, view.bounds.width, view.bounds.height ) ) }; demo_function = { | index | demo_string = "You picked " ++ multi_button_view_states[ index ][ 0 ]; demo_view.refresh }; multi_button_view.bindFunction( demo_function ); multi_button_view.valueState( 2 ); win.layout_( VLayout( [ multi_button_view, stretch: 2 ], [ demo_view, stretch: 1 ] ) ); win.front; )