GUI enabling/disabling of controls: Difference between revisions
JavaFX version added |
|||
Line 858: | Line 858: | ||
{{ |
{{libheader|JavaFX}} |
||
{{works with|Java|8}} |
{{works with|Java|8}} |
||
<lang java> |
<lang java> |
Revision as of 19:20, 3 October 2014
You are encouraged to solve this task according to the task description, using any language you may know.
In addition to fundamental GUI component interaction, an application should dynamically enable and disable GUI components, to give some guidance to the user, and prohibit (inter)actions which are inappropriate in the current state of the application.
The task: Similar to the task GUI component interaction write a program that presents a form with three components to the user: A numeric input field ("Value") and two buttons ("increment" and "decrement").
The field is initialized to zero. The user may manually enter a new value into the field, increment its value with the "increment" button, or decrement the value with the "decrement" button.
The input field should be enabled only when its value is zero. The "increment" button only as long as the field's value is less then 10: When the value 10 is reached, the button should go into a disabled state. Analogously, the "decrement" button should be enabled only as long as the value is greater than zero.
Effectively, the user can now either increment up to 10, or down to zero. Manually entering values outside that range is still legal, but the buttons should reflect that and enable/disable accordingly.
Ada
disabling.adb: <lang Ada>with Ada.Strings.Fixed; with Gtk.Main; with Gtk.Handlers; with Gtk.Button; with Gtk.Window; with Gtk.GEntry; with Gtk.Editable; with Gtk.Box; with Gtk.Widget; with Glib.Values;
procedure Disabling is
type My_Natural is range 0 .. 10;
The_Value : My_Natural := 0;
Main_Window : Gtk.Window.Gtk_Window; Content : Gtk.Box.Gtk_Vbox; Increment_Button : Gtk.Button.Gtk_Button; Decrement_Button : Gtk.Button.Gtk_Button; Entry_Field : Gtk.GEntry.Gtk_Entry;
package Entry_Callbacks is new Gtk.Handlers.Callback (Gtk.GEntry.Gtk_Entry_Record);
package Button_Callbacks is new Gtk.Handlers.Callback (Gtk.Button.Gtk_Button_Record);
package Window_Callbacks is new Gtk.Handlers.Return_Callback (Gtk.Window.Gtk_Window_Record, Boolean);
-- update displayed text procedure Update_Entry is begin Gtk.GEntry.Set_Text (The_Entry => Entry_Field, Text => Ada.Strings.Fixed.Trim (Source => My_Natural'Image (The_Value), Side => Ada.Strings.Both)); end Update_Entry;
procedure Check_Value is begin Gtk.Widget.Set_Sensitive (Gtk.Widget.Gtk_Widget (Decrement_Button), The_Value > 0); Gtk.Widget.Set_Sensitive (Gtk.Widget.Gtk_Widget (Increment_Button), The_Value < 10); Gtk.Widget.Set_Sensitive (Gtk.Widget.Gtk_Widget (Entry_Field), The_Value = 0); end Check_Value;
procedure On_Changed_Text (Object : access Gtk.GEntry.Gtk_Entry_Record'Class; Params : Glib.Values.GValues) is pragma Unreferenced (Params, Object); begin The_Value := My_Natural'Value (Gtk.GEntry.Get_Text (Entry_Field)); Check_Value; Update_Entry; exception when Constraint_Error => The_Value := 0; end On_Changed_Text;
-- make sure that only numbers are entered procedure On_Insert_Text (Object : access Gtk.GEntry.Gtk_Entry_Record'Class; Params : Glib.Values.GValues) is Length : constant Glib.Gint := Glib.Values.Get_Int (Glib.Values.Nth (Params, 2)); Text : constant String := Glib.Values.Get_String (Glib.Values.Nth (Params, 1), Length); begin declare Number : My_Natural; pragma Unreferenced (Number); begin Number := My_Natural'Value (Text); exception when Constraint_Error => -- refuse values that are not parsable Gtk.Handlers.Emit_Stop_By_Name (Object => Object, Name => Gtk.Editable.Signal_Insert_Text); end; end On_Insert_Text;
-- Callback for click event procedure On_Increment_Click (Object : access Gtk.Button.Gtk_Button_Record'Class) is pragma Unreferenced (Object); begin The_Value := The_Value + 1; Check_Value; Update_Entry; end On_Increment_Click;
-- Callback for click event procedure On_Decrement_Click (Object : access Gtk.Button.Gtk_Button_Record'Class) is pragma Unreferenced (Object); begin The_Value := The_Value - 1; Check_Value; Update_Entry; end On_Decrement_Click;
-- Callback for delete event function On_Main_Window_Delete (Object : access Gtk.Window.Gtk_Window_Record'Class) return Boolean is pragma Unreferenced (Object); begin Gtk.Main.Main_Quit; return True; end On_Main_Window_Delete;
begin
Gtk.Main.Init;
Gtk.GEntry.Gtk_New (Widget => Entry_Field); Entry_Callbacks.Connect (Widget => Entry_Field, Name => Gtk.Editable.Signal_Insert_Text, Cb => On_Insert_Text'Access); Entry_Callbacks.Connect (Widget => Entry_Field, Name => Gtk.Editable.Signal_Changed, Cb => On_Changed_Text'Access);
Gtk.Button.Gtk_New (Button => Increment_Button, Label => "Increment"); Gtk.Button.Gtk_New (Button => Decrement_Button, Label => "Decrement");
Button_Callbacks.Connect (Widget => Increment_Button, Name => Gtk.Button.Signal_Clicked, Marsh => Button_Callbacks.To_Marshaller (On_Increment_Click'Access)); Button_Callbacks.Connect (Widget => Decrement_Button, Name => Gtk.Button.Signal_Clicked, Marsh => Button_Callbacks.To_Marshaller (On_Decrement_Click'Access));
Gtk.Box.Gtk_New_Vbox (Box => Content); Gtk.Box.Add (Container => Content, Widget => Entry_Field); Gtk.Box.Add (Container => Content, Widget => Increment_Button); Gtk.Box.Add (Container => Content, Widget => Decrement_Button);
Gtk.Window.Gtk_New (Window => Main_Window); Gtk.Window.Add (Container => Main_Window, Widget => Content);
Window_Callbacks.Connect (Widget => Main_Window, Name => Gtk.Widget.Signal_Delete_Event, Cb => On_Main_Window_Delete'Access); Gtk.Window.Show_All (Widget => Main_Window); Update_Entry;
Gtk.Main.Main;
end Disabling;</lang>
AutoHotkey
<lang AutoHotkey>GUI, Add, Edit, w150 number vValue, 0 ; Number specifies a numbers-only edit field. GUI, Add, button,, Increment GUI, Add, button, xp+70 yp, Decrement ; xp+70 and yp are merely positioning options GUI, Show, w200 y200, Title ; Shows the GUI. Add your own title if you wish SetTimer, EnableDisable, 100 ; Sets EnableDisable to run 10 times per second (100ms) return ; ----------End Auto-Execute Section----------
ButtonIncrement:
GUI, Submit, NoHide ; "Set the contents of each variable to the contents of their corresponding controls without hiding the GUI" If ( value < 10 ) ; Just in case EnableDisable didn't disable the button it in time. Value++ ; Increment Value GUIControl,, Value, %value% ; "Set the text of the control which alters the variable 'value' to the contents of 'value'"
return
ButtonDecrement:
GUI, Submit, Nohide If value > 0 Value-- GuiControl,, Value, %value%
return
EnableDisable:
GUI, Submit, Nohide If ( value < 10 ) GuiControl, enable, Increment Else GuiControl, disable, Increment
If ( value > 0) GuiControl, enable, Decrement Else GuiControl, disable, Decrement
If ( value = 0 ) GuiControl, enable, Edit1 Else GuiControl, disable, Edit1
return
GuiClose:
ExitApp
- Ensures the script ends when the GUI is closed.</lang>
BBC BASIC
<lang bbcbasic> INSTALL @lib$+"WINLIB2"
INSTALL @lib$+"WINLIB5" ES_NUMBER = 8192 form% = FN_newdialog("Rosetta Code", 100, 100, 100, 52, 8, 1000) idInc% = FN_setproc(PROCinc) idDec% = FN_setproc(PROCdec) PROC_static(form%, "Value:", 100, 10, 10, 24, 14, 0) PROC_editbox(form%, "0", 101, 40, 8, 52, 14, ES_NUMBER) PROC_pushbutton(form%, "Increment", idInc%, 7, 30, 40, 16, 0) PROC_pushbutton(form%, "Decrement", idDec%, 52, 30, 40, 16, 0) PROC_showdialog(form%) REPEAT WAIT 1 SYS "GetDlgItemInt", !form%, 101, 0, 1 TO number% SYS "GetDlgItem", !form%, 101 TO hedit% SYS "EnableWindow", hedit%, number% = 0 SYS "GetDlgItem", !form%, idInc% TO hinc% SYS "EnableWindow", hinc%, number% < 10 SYS "GetDlgItem", !form%, idDec% TO hdec% SYS "EnableWindow", hdec%, number% > 0 UNTIL !form% = 0 QUIT DEF PROCinc LOCAL number% SYS "GetDlgItemInt", !form%, 101, 0, 1 TO number% SYS "SetDlgItemInt", !form%, 101, number% + 1, 1 ENDPROC DEF PROCdec LOCAL number% SYS "GetDlgItemInt", !form%, 101, 0, 1 TO number% SYS "SetDlgItemInt", !form%, 101, number% - 1, 1 ENDPROC</lang>
Output:
C++
with Qt 4.4, creating project file with qmake -project, Makefile with qmake -o Makefile <projectfile> and finally make
file task.h
<lang cpp>#ifndef TASK_H
- define TASK_H
- include <QWidget>
class QPushButton ; class QString ; class QLineEdit ; class QLabel ; class QVBoxLayout ; class QHBoxLayout ;
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget( QWidget *parent = 0 ) ;
private slots:
void buttonChange( const QString & ) ; void addField( ) ; void subtractField( ) ;
private :
QVBoxLayout *thisWidgetLayout ; QLabel *instruction ; QPushButton *increment ; QPushButton *decrement ; QLineEdit *entryField ; QHBoxLayout *lowerPart ;
} ;
- endif</lang>
file task.cpp
<lang cpp>#include <QtGui>
- include <QString>
- include "task.h"
MyWidget::MyWidget ( QWidget *parent )
: QWidget( parent ) { thisWidgetLayout = new QVBoxLayout ( this ) ; instruction = new QLabel ; instruction->setText( "Enter a number between 1 and 10 ! Numbers above 10 are decremented, below 0 incremented!" ) ; instruction->setWordWrap( true ) ; lowerPart = new QHBoxLayout ; entryField = new QLineEdit( "0" ) ; increment = new QPushButton( "Increment" ) ; decrement = new QPushButton( "Decrement" ) ; increment->setDefault( true ) ; connect( entryField , SIGNAL ( textChanged ( const QString & ) ) ,
this , SLOT ( buttonChange( const QString & )) ) ;
connect( entryField , SIGNAL ( textEdited ( const QString & ) ) ,
this , SLOT ( buttonChange( const QString & )) ) ;
connect( increment , SIGNAL ( clicked( ) ) , this ,
SLOT ( addField( ) )) ;
connect( decrement , SIGNAL ( clicked( ) ) , this ,
SLOT ( subtractField( ))) ;
lowerPart->addWidget( entryField ) ; lowerPart->addWidget( increment ) ; lowerPart->addWidget( decrement ) ; thisWidgetLayout->addWidget( instruction ) ; thisWidgetLayout->addLayout( lowerPart ) ; setLayout( thisWidgetLayout ) ;
}
void MyWidget::buttonChange( const QString & text ) {
bool ok ; increment->setEnabled( text.toInt( &ok, 10 ) < 10 ) ; increment->setDisabled( text.toInt( &ok, 10 ) > 9 ) ; decrement->setEnabled( text.toInt( &ok, 10 ) > 0 ) ; decrement->setDisabled( text.toInt( &ok, 10 ) <= 0 ) ; if ( ! ( text == "0" ) ) entryField->setReadOnly( true ) ;
}
void MyWidget::addField( ) {
bool ok ; int number = entryField->text( ).toInt( &ok , 10 ) ; number++ ; entryField->setText( QString("%1").arg( number )) ;
}
void MyWidget::subtractField( ) {
bool ok ; int number = entryField->text( ).toInt( &ok , 10 ) ; number-- ; entryField->setText( QString("%1").arg( number )) ;
}</lang>
main.cpp
<lang cpp>#include <QApplication>
- include "task.h"
int main( int argc, char *argv[ ] ) {
QApplication app( argc , argv ) ; MyWidget theWidget ; theWidget.show( ) ; return app.exec( ) ;
}</lang>
C_sharp
Using Windows Forms; compile with csc -t:winexe Program.cs (on MS.NET) or gmcs -t:winexe Program.cs (on Mono) <lang c_sharp>using System; using System.ComponentModel; using System.Windows.Forms;
class RosettaInteractionForm : Form {
// Model used for DataBinding. // Notifies bound controls about Value changes. class NumberModel: INotifyPropertyChanged { // initialize event with empty delegate to avoid checks on null public event PropertyChangedEventHandler PropertyChanged = delegate {};
int _value; public int Value { get { return _value; } set { _value = value; // Notify bound control about value change PropertyChanged(this, new PropertyChangedEventArgs("Value")); } } }
NumberModel model = new NumberModel{ Value = 0}; RosettaInteractionForm() { //MaskedTextBox is a TextBox variety with built-in input validation var tbNumber = new MaskedTextBox { Mask="0000", // allow 4 decimal digits only ResetOnSpace = false, // don't enter spaces Dock = DockStyle.Top // place at the top of form }; // bound TextBox.Text to NumberModel.Value; tbNumber.DataBindings.Add("Text", model, "Value"); var enabledIfZero = new Binding("Enabled", model, "Value"); EnableControlWhen(tbNumber, value => value == 0);
var btIncrement = new Button{Text = "Increment", Dock = DockStyle.Bottom}; btIncrement.Click += delegate { model.Value++; }; EnableControlWhen(btIncrement, value => value < 10); var btDecrement = new Button{Text = "Decrement", Dock = DockStyle.Bottom}; btDecrement.Click += delegate { model.Value--; }; EnableControlWhen(btDecrement, value => value > 0); Controls.Add(tbNumber); Controls.Add(btIncrement); Controls.Add(btDecrement); }
// common part of creating bindings for Enabled property void EnableControlWhen(Control ctrl, Func<int, bool> predicate) { // bind Control.Enabled to NumberModel.Value var enabledBinding = new Binding("Enabled", model, "Value"); // Format event is called when model value should be converted to Control value. enabledBinding.Format += (sender, args) => { // Enabled property is of bool type. if (args.DesiredType != typeof(bool)) return; // set resulting value by applying condition args.Value = predicate((int)args.Value); }; // as a result, control will be enabled if predicate returns true ctrl.DataBindings.Add(enabledBinding); }
static void Main() { Application.Run(new RosettaInteractionForm()); }
}</lang>
Delphi
<lang delphi>type
TForm1 = class(TForm) MaskEditValue: TMaskEdit; // Set Editmask on: "99;0; " SpeedButtonIncrement: TSpeedButton; SpeedButtonDecrement: TSpeedButton; procedure MaskEditValueChange(Sender: TObject); procedure SpeedButtonDecrementClick(Sender: TObject); procedure SpeedButtonIncrementClick(Sender: TObject); end;
var
Form1: TForm1;
implementation
procedure TForm1.MaskEditValueChange(Sender: TObject); begin
TMaskEdit(Sender).Enabled := StrToIntDef(Trim(TMaskEdit(Sender).Text), 0) = 0; SpeedButtonIncrement.Enabled := StrToIntDef(Trim(TMaskEdit(Sender).Text), 0) < 10; SpeedButtonDecrement.Enabled := StrToIntDef(Trim(TMaskEdit(Sender).Text), 0) > 0;
end;
procedure TForm1.SpeedButtonDecrementClick(Sender: TObject); begin
MaskEditValue.Text := IntToStr(Pred(StrToIntDef(Trim(MaskEditValue.Text), 0)));
end;
procedure TForm1.SpeedButtonIncrementClick(Sender: TObject); begin
MaskEditValue.Text := IntToStr(Succ(StrToIntDef(Trim(MaskEditValue.Text), 0)));
end;</lang>
Fantom
<lang fantom>using fwt using gfx
class Main {
public static Void main () { Window { textField := Text { text = "0" } incButton := Button { text = "increment" onAction.add |Event e| { if (textField.text != "") { try { Int value := textField.text.toInt if (value < 10) value += 1 textField.text = value.toStr } catch {} } } } decButton := Button { text = "decrement" onAction.add |Event e| { if (textField.text != "") { try { Int value := textField.text.toInt if (value > 0) value -= 1 textField.text = value.toStr } catch {} } } } // add a listener for modifications to the text field // which updates the button visibilities textField.onModify.add |Event e| { try { Int value := textField.text.toInt incButton.enabled = (value < 10) // update whether button can be used decButton.enabled = (value > 0) // update whether button can be used } catch textField.text = "0" // reset if not an int } EdgePane { top = textField left = incButton right = decButton }, }.open }
}</lang>
Icon and Unicon
This uses the Unicon specific graphics library.
<lang Unicon> import gui $include "guih.icn"
class MessageDialog : Dialog (message)
method component_setup () label := Label ("label="||message, "pos=20,20") add (label) button := TextButton("label=OK", "pos=100,60") button.connect (self, "dispose", ACTION_EVENT) add (button)
connect (self, "dispose", CLOSE_BUTTON_EVENT) attrib ("size=200,100", "bg=light gray") end
initially (message) self.Dialog.initially() self.message := message
end
class WindowApp : Dialog (button1, button2, field, value)
method set_enabled () if value = 0 then field.clear_is_shaded () else field.set_is_shaded () if value <= 0 then button2.set_is_shaded () else button2.clear_is_shaded () if value >= 10 then button1.set_is_shaded () else button1.clear_is_shaded () end
method increment () value +:= 1 field.set_contents (string(value)) set_enabled () end
method decrement () value -:= 1 field.set_contents (string(value)) set_enabled () end
method handle_text_field () if not(integer(field.get_contents ())) then { warning := MessageDialog ("Not a number") warning.show_modal () field.set_contents (string(value)) } else { n := integer (field.get_contents ()) if not (0 <= n <= 10) then { warning := MessageDialog ("Not in range") warning.show_modal () field.set_contents (string(value)) } } value := integer (field.get_contents ()) # must be ok set_enabled () end
method component_setup () value := 0 field := TextField("contents="||value, "pos=20,20", "size=150") field.connect (self, "handle_text_field", TEXTFIELD_CHANGED_EVENT) add (field) button1 := TextButton("label=Increment", "pos=20,60", "size=70") button1.connect (self, "increment", ACTION_EVENT) add (button1) button2 := TextButton("label=Decrement", "pos=100,60", "size=70") button2.connect (self, "decrement", ACTION_EVENT) add (button2)
connect (self, "dispose", CLOSE_BUTTON_EVENT) attrib ("size=200,100", "bg=light gray") set_enabled () end
end
procedure main ()
w := WindowApp () w.show_modal ()
end </lang>
J
<lang J>task_run=: wd bind (noun define)
pc task nosize; xywh 6 30 48 12;cc decrement button;cn "-"; xywh 6 18 48 12;cc increment button;cn "+"; xywh 6 6 48 12;cc Value edit; set Value 0; pas 6 6;pcenter; pshow;
)
task_close=: wd bind 'pclose'
task_Value_button=: update=: verb define
wd 'set Value ',":n=.{.0".Value wd 'setenable Value ',":n=0 wd 'setenable increment ',":n<10 wd 'setenable decrement ',":n>0
)
task_increment_button=:verb define
update Value=:":1+0".Value
) task_decrement_button=:verb define
update Value=:":_1+0".Value
)</lang>
Example use:
<lang> task_run</lang>
Java
<lang java>import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener;
import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingUtilities;
public class Interact extends JFrame{ final JTextField numberField; final JButton incButton, decButton;
public Interact(){ //stop the GUI threads when the user hits the X button setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
numberField = new JTextField(); incButton = new JButton("Increment"); decButton = new JButton("Decrement");
numberField.setText("0");//start at 0 decButton.setEnabled(false);//we're already at 0
//listen for button presses in the text field numberField.addKeyListener(new KeyListener(){ @Override public void keyTyped(KeyEvent e) { //if the entered character is not a digit if(!Character.isDigit(e.getKeyChar())){ //eat the event (i.e. stop it from being processed) e.consume(); }else if(Character.isDigit(e.getKeyChar())){ //This method is executed from the event thread and updating the GUI //from there doesn't always work. invokeLater will ensure that the //GUI is updated SwingUtilities.invokeLater(new Runnable() { @Override public void run() { String text = numberField.getText(); if(text.isEmpty()){//default to 0 when all text is erased numberField.setText("0"); decButton.setEnabled(false); incButton.setEnabled(true); return; } if(Long.valueOf(text) <= 0){ decButton.setEnabled(false); incButton.setEnabled(true); }else if(Long.valueOf(text) >= 10){ incButton.setEnabled(false); decButton.setEnabled(true); }else{ incButton.setEnabled(true); decButton.setEnabled(true); } } }); } } @Override public void keyReleased(KeyEvent e){} @Override public void keyPressed(KeyEvent e){ //backspace and delete don't register in keyTyped because they don't //display a Unicode character, so they must be handled here if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE || e.getKeyCode() == KeyEvent.VK_DELETE){ SwingUtilities.invokeLater(new Runnable() { @Override public void run() { String text = numberField.getText(); if(text.isEmpty()){ numberField.setText("0"); decButton.setEnabled(false); incButton.setEnabled(true); return; } if(Long.valueOf(text) <= 0){ decButton.setEnabled(false); incButton.setEnabled(true); }else if(Long.valueOf(text) >= 10){ incButton.setEnabled(false); decButton.setEnabled(true); }else{ incButton.setEnabled(true); decButton.setEnabled(true); } } }); } } });
//listen for button clicks on the increment button incButton.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { String text = numberField.getText(); numberField.setText((Long.valueOf(text) + 1) + ""); if(Long.valueOf(text) + 1 >= 10){ incButton.setEnabled(false); }
if(Long.valueOf(text) + 1 > 0){ decButton.setEnabled(true); } } });
//listen for button clicks on the random button decButton.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { String text = numberField.getText(); numberField.setText((Long.valueOf(text) - 1) + ""); if(Long.valueOf(text) - 1 <= 0){ decButton.setEnabled(false); }
if(Long.valueOf(text) - 1 < 10){ incButton.setEnabled(true); } } });
//arrange the components in a grid with 2 rows and 1 column setLayout(new GridLayout(2, 1));
//a secondary panel for arranging both buttons in one grid space in the window JPanel buttonPanel = new JPanel();
//the buttons are in a grid with 1 row and 2 columns buttonPanel.setLayout(new GridLayout(1, 2)); //add the buttons buttonPanel.add(incButton); buttonPanel.add(decButton);
//put the number field on top of the buttons add(numberField); add(buttonPanel); //size the window appropriately pack();
}
public static void main(String[] args){ new Interact().setVisible(true); } }</lang>
<lang java> import javafx.application.Application; import javafx.beans.property.LongProperty; import javafx.beans.property.SimpleLongProperty; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.util.converter.NumberStringConverter;
public class InteractFX extends Application {
public static void main(String[] args) { launch(args); }
@Override public void start(Stage stage) throws Exception {
TextField input = new TextField("0"){ // only accept numbers as input @Override public void replaceText(int start, int end, String text) { if (text.matches("[0-9]*")) { super.replaceText(start, end, text); } }
// only accept numbers on copy+paste @Override public void replaceSelection(String text) { if (text.matches("[0-9]*")) { super.replaceSelection(text); } } };
// when the textfield is empty, replace text with "0" input.textProperty().addListener((observable, oldValue, newValue)->{ if(newValue == null || newValue.trim().isEmpty()){ input.setText("0"); } });
// get a bi-directional bound long-property of the input value LongProperty inputValue = new SimpleLongProperty(); input.textProperty().bindBidirectional(inputValue, new NumberStringConverter());
// textfield is disabled when the current value is other than "0" input.disableProperty().bind(inputValue.isNotEqualTo(0));
Button increment = new Button("Increment"); increment.setOnAction(event-> inputValue.set(inputValue.get() + 1));
// incr-button is disabled when input is >= 0 increment.disableProperty().bind(inputValue.greaterThanOrEqualTo(10));
Button decrement = new Button("Decrement"); decrement.setOnAction(event-> inputValue.set(inputValue.get() - 1));
// decrement button is disabled when input is <=0 decrement.disableProperty().bind(inputValue.lessThanOrEqualTo(0));
// layout VBox root = new VBox(); root.getChildren().add(input); HBox buttons = new HBox(); buttons.getChildren().addAll(increment,decrement); root.getChildren().add(buttons);
stage.setScene(new Scene(root)); stage.sizeToScene(); stage.show(); }
} </lang>
Julia
<lang julia> using Tk w = Toplevel("GUI enabling/disabling") fr = Frame(w) pack(fr, {:expand=>true, :fill => "both"})
value = Entry(fr) increment = Button(fr, "+") decrement = Button(fr, "-")
formlayout(value, "Value:") formlayout(increment, " ") formlayout(decrement, " ")
- value stores a string
set_value(value, "0") ## The field is initialized to zero. get(value::Tk_Entry) = try parseint(get_value(value)) catch e nothing end
function update()
cur_value = get(value) set_enabled(value, isa(cur_value, Integer) && cur_value == 0) set_enabled(increment, isa(cur_value, Integer) && cur_value < 10) set_enabled(decrement, isa(cur_value, Integer) && cur_value > 0)
end
crement = function(step)
set_enabled(value, true) set_value(value, string(get(value) + step)) update()
end tk_bind(increment, "command", path -> crement(1)) tk_bind(decrement, "command", path -> crement(-1)) update()
function validate_command(path, P)
try if length(P) > 0 parseint(P); update() end tcl("expr", "TRUE") catch e tcl("expr", "FALSE") end
end function invalid_command(path, W)
println("Invalid value") tcl(W, "delete", "@0", "end")
end
tk_configure(value, {:validate=>"focusout", :validatecommand=>validate_command, :invalidcommand=>invalid_command })
</lang>
Liberty BASIC
<lang lb>nomainwin
textbox #demo.val, 20, 50, 90, 24 button #demo.dec, "Decrement", [btnDecrement], UL, 20, 90, 90, 24 button #demo.inc, "Increment", [btnIncrement], UL, 20, 120, 90, 24 statictext #demo.txt, "Positive or negative whole numbers only.", 20, 170, 240, 24 open "Rosetta Task: GUI enabling/disabling of controls" for window as #demo #demo "trapclose [quit]" #demo.val 0 #demo.dec "!disable"
wait
[quit]
close #demo
end
[btnDecrement]
validNum = validNum() if validNum = 0 then #demo.val "!contents? nVal$" notice nVal$;" does not appear to be a valid whole number." else #demo.val "!contents? nVal" if nVal > 0 then nVal = nVal - 1 end if end if #demo.val nVal call disEnableControls nVal
wait
[btnIncrement]
validNum = validNum() if validNum = 0 then #demo.val "!contents? nVal$" notice nVal$;" does not appear to be a valid whole number." else #demo.val "!contents? nVal" if nVal < 10 then nVal = nVal + 1 end if end if #demo.val nVal call disEnableControls nVal
wait
Function validNum()
validNum$ = "0123456789" #demo.val "!contents? nVal$" nVal$ = trim$(nVal$) if left$(nVal$, 1) = "-" then neg = 1 nVal$ = mid$(nVal$, 2) else neg = 0 end if validNum = 1 for i = 1 to len(nVal$) if instr(validNum$, mid$(nVal$, i, 1)) = 0 then validNum = 0 end if next i
End Function
Sub disEnableControls nVal
if nVal > 9 then #demo.inc "!disable" else #demo.inc "!enable" end if if nVal < 1 then #demo.dec "!disable" else #demo.dec "!enable" end if if nVal = 0 then #demo.val "!enable" else #demo.val "!disable" end if
End Sub</lang>
Mathematica
<lang mathematica>Manipulate[Null, {{value, 0},
InputField[Dynamic[value], Number, Enabled -> Dynamic[value == 0]] &}, Row@{Button["increment", value++, Enabled -> Dynamic[value < 10]], Button["decrement", value--, Enabled -> Dynamic[value > 0]]}]</lang>
NewLISP
<lang NewLISP>; file: gui-enable.lsp
- url
- http://rosettacode.org/wiki/GUI_enabling/disabling_of_controls
- author
- oofoe 2012-02-02
- Load library and initialize GUI server
(load (append (env "NEWLISPDIR") "/guiserver.lsp")) (gs:init)
- The "interlock" function maintains GUI consistency by disabling all
- controls, then selectively re-enabling them depending on the value
- in the textbox.
(define (interlock)
(gs:disable 'value 'increment 'decrement) (let ((v (int (gs:get-text 'value)))) (if (= 0 v) (gs:enable 'value)) (if (< v 10) (gs:enable 'increment)) (if (< 0 v) (gs:enable 'decrement)) ))
- Callbacks.
(define (update f)
(gs:set-text 'value (string (f (int (gs:get-text 'value)) 1))) (interlock))
(define (incrementing id) (update +))
(define (decrementing id) (update -))
(define (valuing id) (interlock))
- Create main window frame and set layout direction.
(gs:frame 'main 100 100 200 75 "GUI Enable") (gs:set-flow-layout 'main "center" 4 4)
- Create and add widgets.
(gs:button 'decrement 'decrementing "-" 30) (gs:text-field 'value 'valuing 8) (gs:set-text 'value "0") (gs:button 'increment 'incrementing "+" 30) (gs:add-to 'main 'decrement 'value 'increment)
- Show main window.
(gs:set-visible 'main true)
- Start event loop.
(gs:listen)
(exit)</lang>
Screenshot:
Nimrod
<lang nimrod>import
gtk2, strutils, glib2
var valu: int = 0 var chngd_txt_hndler: gulong = 0
proc thisCheckBtns # forward declaration
proc thisDestroy(widget: pWidget, data: pgpointer){.cdecl.} =
main_quit()
nimrod_init() var window = window_new(gtk2.WINDOW_TOPLEVEL) var content = vbox_new(TRUE,10) var hbox1 = hbox_new(TRUE,10) var entry_fld = entry_new() entry_fld.set_text("0") var btn_quit = button_new("Quit") var btn_inc = button_new("Increment") var btn_dec = button_new("Decrement") add(hbox1,btn_inc) add(hbox1,btn_dec) pack_start(content, entry_fld, TRUE, TRUE, 0) pack_start(content, hbox1, TRUE, TRUE, 0) pack_start(content, btn_quit, TRUE, TRUE, 0) set_border_width(Window, 5) add(window, content)
proc thisInc(widget: pWidget, data: pgpointer){.cdecl.} =
inc(valu) entry_fld.set_text($valu) thisCheckBtns()
proc thisDec(widget: pWidget, data: pgpointer){.cdecl.} =
dec(valu) entry_fld.set_text($valu) thisCheckBtns()
proc thisTextChanged(widget: pWidget, data: pgpointer) {.cdecl.} =
#signal_handler_block(entry_fld, chngd_txt_hndler) try: valu = parseInt($entry_fld.get_text()) except EInvalidValue: valu = 0 entry_fld.set_text($valu) #signal_handler_unblock(entry_fld, chngd_txt_hndler) #signal_emit_stop(entry_fld, signal_lookup("changed",TYPE_EDITABLE()),0) thisCheckBtns()
proc thisCheckBtns =
set_sensitive(btn_inc, valu < 10) set_sensitive(btn_dec, valu > 0) set_sensitive(entry_fld, valu == 0)
discard signal_connect(window, "destroy",
SIGNAL_FUNC(thisDestroy), nil)
discard signal_connect(btn_quit, "clicked",
SIGNAL_FUNC(thisDestroy), nil)
discard signal_connect(btn_inc, "clicked",
SIGNAL_FUNC(thisInc), nil)
discard signal_connect(btn_dec, "clicked",
SIGNAL_FUNC(thisDec), nil)
chngd_txt_hndler = signal_connect(entry_fld, "changed",
SIGNAL_FUNC(thisTextChanged), nil)
show_all(window) thisCheckBtns() main()</lang>
Perl 6
Extremely basic implementation using the GTK library. <lang perl6>use GTK::Simple;
my GTK::Simple::App $app .= new( title => 'Controls Enable \ Disable' );
$app.border_width = 20;
$app.set_content(
GTK::Simple::HBox.new( my $inc = GTK::Simple::Button.new( label => ' + ' ), my $value = GTK::Simple::Entry.new, my $dec = GTK::Simple::Button.new( label => ' - ' ) )
);
$value.changed.tap: {
$value.text.=subst(/\D/, ); $inc.sensitive = $value.text < 10; $dec.sensitive = $value.text > 0;
}
$value.text = 0;
$inc.clicked.tap: { $value.text += 1 } $dec.clicked.tap: { $value.text -= 1 }
$app.run;</lang>
PicoLisp
The standard PicoLisp GUI is HTTP based. Connect your browser to http://localhost:8080 after starting the following script. <lang PicoLisp>#!/usr/bin/picolisp /usr/lib/picolisp/lib.l
(load "@ext.l" "@lib/http.l" "@lib/xhtml.l" "@lib/form.l")
(de start ()
(and (app) (zero *Number)) (action (html 0 "Enable/Disable" "@lib.css" NIL (form NIL (gui '(+Var +Able +NumField) '*Number '(=0 *Number) 20 "Value") (gui '(+Able +JS +Button) '(> 10 *Number) "increment" '(inc '*Number) ) (gui '(+Able +JS +Button) '(gt0 *Number) "decrement" '(dec '*Number) ) ) ) ) )
(server 8080 "!start") (wait)</lang>
Prolog
Works with SWI-Prolog and XPCE. <lang Prolog>dialog('GUI_Interaction',
[ object :=
GUI_Interaction, parts := [ GUI_Interaction := dialog('Rosetta Code'), Name := label(name, 'Value :'), Input_field := text_item(input_field, '0'), Increment := button(increment), Decrement := button(decrement) ], modifications := [ Input_field := [ label := 'Value :', length := 28, show_label := @off ], Decrement := [active := @off] ], layout := [ area(Name, area(50, 26, 25, 24)), area(Input_field, area(95, 24, 200, 24)), area(Increment, area(50, 90, 80, 24)), area(Decrement, area(230, 90, 80, 24)) ], behaviour :=
[ Increment := [ message := message(@prolog, increment, Increment, Decrement, Input_field ) ], Decrement := [ message := message(@prolog, decrement, Increment, Decrement, Input_field) ], Input_field := [ message := message(@prolog, input, Increment, Decrement, @receiver, @arg1) ] ]
]).
gui_component :- make_dialog(S, 'GUI_Interaction'), send(S, open).
increment(Incr, Decr, Input) :-
get(Input, selection, V),
atom_number(V, Val),
Val1 is Val + 1,
send(Input, selection, Val1),
test(Val1, Incr, Decr, Input).
decrement(Incr, Decr, Input) :- get(Input, selection, V), atom_number(V, Val), Val1 is Val - 1, send(Input, selection, Val1), test(Val1, Incr, Decr, Input).
input(Incr, Decr, Input, Selection) :- catch( (term_to_atom(T, Selection), number(T)), _, ( send(@display, inform, 'Please type a number !'), T = 0, send(Input,selection, T))), test(T, Incr, Decr, Input).
test(V, Incr, Decr, Input) :-
( V = 0 -> send(Input, active, @on); send(Input, active, @off)),
send(Incr, active, @on),
send(Decr, active, @on),
( V < 1
-> send(Decr, active, @off)
; V > 9
-> send(Incr, active, @off)).
</lang>
PureBasic
<lang PureBasic>Enumeration
#TextGadget #AddButton #SubButton
EndEnumeration
Procedure UpdateGadgets(Value,UpdateValue=0)
Overmax=0: UnderMin=0 If Value>=10 Overmax=1 ElseIf Value<=0 UnderMin=1 EndIf DisableGadget(#AddButton,Overmax) DisableGadget(#SubButton,UnderMin) If UpdateValue SetGadgetText(#TextGadget,Str(Value)) EndIf
EndProcedure
If OpenWindow(0,#PB_Ignore,#PB_Ignore,110,70,"PB-GUI",#PB_Window_SystemMenu)
StringGadget(#TextGadget,10,10,90,20,"") ButtonGadget(#AddButton,10,40,30,20,"+") ButtonGadget(#SubButton,70,40,30,20,"-") UpdateGadgets(Value,1) Repeat Event=WaitWindowEvent() If Event=#PB_Event_Gadget Gadget=EventGadget() Select Gadget Case #AddButton Value+1 UpdateGadgets(Value,1) Case #SubButton Value-1 UpdateGadgets(Value,1) Default EType=EventType() If EType=#PB_EventType_Change Value=Val(GetGadgetText(#TextGadget)) UpdateGadgets(Value) EndIf EndSelect EndIf Until Event=#PB_Event_CloseWindow
EndIf</lang>
R
<lang R> library(gWidgets) options(guiToolkit="RGtk2") ## using gWidgtsRGtk2
w <- gwindow("Disable components")
g <- ggroup(cont=w, horizontal=FALSE) e <- gedit("0", cont=g, coerce.with=as.numeric) bg <- ggroup(cont=g)
down_btn <- gbutton("-", cont=bg) up_btn <- gbutton("+", cont=bg)
update_ctrls <- function(h,...) {
val <- svalue(e) enabled(down_btn) <- val >= 0 enabled(up_btn) <- val <= 10
}
rement <- function(h,...) {
svalue(e) <- svalue(e) + h$action update_ctrls(h,...)
}
addHandlerChanged(e, handler=update_ctrls) addHandlerChanged(down_btn, handler=rement, action=-1) addHandlerChanged(up_btn, handler=rement, action=1) </lang>
Racket
<lang racket>
- lang racket/gui
(define frame (new frame% [label "Interaction Demo"]))
(define (changed . _)
(define s (send inp get-value)) (define v (string->number s)) (unless v (set! v (or (string->number (regexp-replace* #rx"[^0-9]+" s "")) 0)) (send inp set-value (~a v))) (send inc-b enable (< v 10)) (send dec-b enable (> v 0)) (send inp enable (zero? v)))
(define ((change-value f) . _)
(send inp set-value (number->string (f (string->number (send inp get-value))))) (changed))
(define inp
(new text-field% [label "Value"] [parent frame] [init-value "0"] [callback changed]))
(define buttons (new horizontal-pane% [parent frame])) (define inc-b
(new button% [parent buttons] [label "Increment"] [callback (change-value add1)]))
(define dec-b
(new button% [parent buttons] [label "Decrement"] [callback (change-value sub1)]))
(send frame show #t) </lang>
Ruby
<lang ruby>Shoes.app do
@number = edit_line @number.change {update_controls}
@incr = button('Increment') {update_controls(@number.text.to_i + 1)} @decr = button('Decrement') {update_controls(@number.text.to_i - 1)}
def update_controls(value = @number.text.to_i) @number.text = value @incr.state = value.to_i >= 10 ? "disabled" : nil @decr.state = value.to_i <= 0 ? "disabled" : nil end
update_controls 0
end</lang>
Scala
<lang Scala>import swing.{ BoxPanel, Button, GridPanel, Orientation, Swing, TextField } import swing.event.{ ButtonClicked, Key, KeyPressed, KeyTyped }
object Enabling extends swing.SimpleSwingApplication {
def top = new swing.MainFrame { title = "Rosetta Code >>> Task: GUI enabling/disabling of controls | Language: Scala"
val numberField = new TextField { text = "0" // start at 0 horizontalAlignment = swing.Alignment.Right }
val (incButton, decButton) = (new Button("Increment"), // Two variables initialized new Button("Decrement") { enabled = false })
// arrange buttons in a grid with 1 row, 2 columns val buttonPanel = new GridPanel(1, 2) { contents ++= List(incButton, decButton) }
// arrange text field and button panel in a grid with 2 row, 1 column contents = new BoxPanel(Orientation.Vertical) { contents ++= List(numberField, buttonPanel) }
val specialKeys = List(Key.BackSpace, Key.Delete)
// listen for keys pressed in numberField and button clicks listenTo(numberField.keys, incButton, decButton) reactions += { case kt: KeyTyped => if (kt.char.isDigit) // if the entered char is a digit ... Swing.onEDT(switching) // ensure GUI-updating else kt.consume // ... eat the event (i.e. stop it from being processed) case KeyPressed(_, kp, _, _) if (!specialKeys.contains(kp)) => Swing.onEDT(switching) // ensure GUI-updating case ButtonClicked(`incButton`) => numberField.text = (numberField.text.toLong + 1).toString switching case ButtonClicked(`decButton`) => numberField.text = (numberField.text.toLong - 1).toString switching }
def switching = { val n = (if (numberField.text.isEmpty()) "0" else numberField.text).toLong numberField.text = n.toString numberField.enabled = n == 0 incButton.enabled = n < 10 decButton.enabled = n > 0 } centerOnScreen() } // def top(
}</lang>
Smalltalk
<lang smalltalk>|top input vh incButton decButton|
vh := ValueHolder with:0.
top := StandardSystemView label:'Rosetta GUI interaction'. top extent:300@100. top add:((Label label:'Value:') origin: 0 @ 10 corner: 100 @ 40). top add:(input := EditField origin: 102 @ 10 corner: 1.0 @ 40). input model:(TypeConverter onNumberValue:vh). input enableChannel:(BlockValue with:[:v | v = 0] argument:vh).
top add:((incButton := Button label:'Inc') origin: 10 @ 50 corner: 100 @ 80). top add:((decButton := Button label:'Dec') origin: 110 @ 50 corner: 210 @ 80).
incButton action:[ vh value: (vh value + 1) ]. incButton enableChannel:(BlockValue with:[:v | v < 10] argument:vh). decButton action:[ vh value: (vh value - 1) ]. decButton enableChannel:(BlockValue with:[:v | v > 0] argument:vh).
top open</lang>
Tcl
<lang tcl>package require Tk
- Model
set field 0
- View
place [ttk::frame .bg] -relwidth 1 -relheight 1; # Hack to make things look nice pack [ttk::labelframe .val -text "Value"] pack [ttk::entry .val.ue -textvariable field \ -validate key -invalidcommand bell \ -validatecommand {string is integer %P}] pack [ttk::button .inc -text "increment" -command up] pack [ttk::button .dec -text "decrement" -command down]
- Controller
proc up {} {
global field incr field
} proc down {} {
global field incr field -1
}
- Attach this controller to the Model; easier than manual calling
trace add variable field write updateEnables proc updateEnables {args} {
global field .inc state [expr {$field < 10 ? "!disabled" : "disabled"}] .dec state [expr {$field > 0 ? "!disabled" : "disabled"}]
} updateEnables; # Force initial state of buttons</lang>
Visual Basic
In VB, windows are usually created in the IDE. The generated code is hidden from the user unless viewed outside of VB. For the sake of this task, I have included that code.
(Also, this sort of task would typically be performed by a "spinner" or "up-down" control, not by buttons.)
<lang vb>VERSION 5.00 Begin VB.Form Form1
Caption = "Form1" ClientHeight = 2265 ClientLeft = 60 ClientTop = 600 ClientWidth = 2175 LinkTopic = "Form1" ScaleHeight = 2265 ScaleWidth = 2175 StartUpPosition = 3 'Windows Default Begin VB.CommandButton cmdDec Caption = "Decrement" Enabled = 0 'False Height = 495 Left = 120 TabIndex = 2 Top = 1680 Width = 1215 End Begin VB.CommandButton cmdInc Caption = "Increment" Height = 495 Left = 120 TabIndex = 1 Top = 1080 Width = 1215 End Begin VB.TextBox txtValue Height = 495 Left = 120 TabIndex = 0 Text = "0" Top = 240 Width = 1215 End
End Attribute VB_Name = "Form1" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False '-----user-written code begins here; everything above this line is hidden in the GUI----- Private Sub cmdDec_Click()
If Val(txtValue.Text) > 0 Then txtValue.Text = Val(txtValue.Text) - 1
End Sub
Private Sub cmdInc_Click()
If Val(txtValue.Text) < 10 Then txtValue.Text = Val(txtValue.Text) + 1
End Sub
Private Sub txtValue_Change()
Select Case Val(txtValue.Text) Case Is < 0 txtValue.Enabled = False cmdInc.Enabled = True cmdDec.Enabled = False Case Is > 9 txtValue.Enabled = False cmdInc.Enabled = False cmdDec.Enabled = True Case 0 txtValue.Enabled = True cmdInc.Enabled = True cmdDec.Enabled = False Case Else txtValue.Enabled = False cmdInc.Enabled = True cmdDec.Enabled = True End Select
End Sub</lang>
- Programming Tasks
- GUI
- Ada
- GtkAda
- AutoHotkey
- BBC BASIC
- C++
- C sharp
- Delphi
- Fantom
- Unicon
- J
- Java
- JavaFX
- Julia
- Liberty BASIC
- Mathematica
- NewLISP
- Nimrod
- Gtk2
- Perl 6
- PicoLisp
- Prolog
- PureBasic
- R
- Racket
- Ruby
- Shoes
- Scala
- Scala Implementations
- Smalltalk
- Tcl
- Tk
- PARI/GP/Omit
- Retro/Omit
- Visual Basic
- ACL2/Omit
- Applesoft BASIC/Omit
- AWK/Omit
- Batch File/Omit
- Blast/Omit
- Brainf***/Omit
- GUISS/Omit
- Integer BASIC/Omit
- Lilypond/Omit
- Logtalk/Omit
- Lotus 123/Omit
- Maxima/Omit
- PostScript/Omit