Tree — Scrollable tree selection
Tree
( | string label , |
itemList
items
); |
items
the items contained in the tree
itemList ::=
[
item
[ , item ]
[ , item ]
...
]
item ::=
string |
`item(
[ `id( string ),]
string
[ , true | false ]
[ , itemList ]
) The boolean parameter inside `item() indicates whether or not the respective tree item should be opened by default - if it has any subitems and if the respective UI is capable of closing and opening subtrees. If the UI cannot handle this, all subtrees will always be open.
A tree widget provides a selection from a hierarchical tree structure. The semantics are very much like those of a SelectionBox. Unlike the SelectionBox, however, tree items may have subitems that in turn may have subitems etc.
Each item has a label string, optionally preceded by an ID. If the item has subitems, they are specified as a list of items after the string.
The tree widget will not perform any sorting on its own: The items are always sorted by insertion order. The application needs to handle sorting itself, if desired.
Note: The Qt version of the Wizard widget also provides a built-in tree with an API that is (sometimes) easier to use.
// Simple tree example
{
UI::OpenDialog(
`VBox(
`Tree(`id(`dest_dir),
"Select destination directory:",
[
`item(`id(`root), "/" , true,
[
`item(`id(`etc), "etc",
[
`item("opt"),
`item("SuSEconfig"),
`item("X11")
]
),
`item("usr", false,
[
"bin",
"lib",
`item("share",
[
"man",
"info",
"emacs"
]
),
`item(`id(`usr_local),"local"),
`item("X11R6",
[
"bin",
"lib",
"share",
"man",
"etc"
]
)
]
),
`item(`id(`opt), "opt", true,
[
"kde",
"netscape",
"Office51"
]
),
`item("home"),
"work",
`item(`id(`other), "<other>")
]
)
] ),
`HBox(
`PushButton(`id(`sel_opt), `opt(`hstretch), "/&opt" ),
`PushButton(`id(`sel_usr), `opt(`hstretch), "/&usr" ),
`PushButton(`id(`sel_usr_local), `opt(`hstretch), "/usr/&local" )
),
`PushButton(`id(`ok), `opt(`default), "&OK")
)
);
any id = nil;
repeat
{
id = UI::UserInput();
if ( id == `sel_usr) UI::ChangeWidget(`dest_dir, `CurrentItem, "usr" );
else if ( id == `sel_usr_local) UI::ChangeWidget(`dest_dir, `CurrentItem, `usr_local );
else if ( id == `sel_opt) UI::ChangeWidget(`dest_dir, `CurrentItem, `opt );
} until ( id == `ok );
// Get the input from the tree.
//
// Notice: The return value of UI::UserInput() does NOT return this value!
// Rather, it returns the ID of the widget (normally the PushButton)
// that caused UI::UserInput() to return.
any dest_dir = UI::QueryWidget(`dest_dir, `CurrentItem);
y2debug( "Selected: %1", dest_dir );
if ( dest_dir == nil )
dest_dir = "";
// Close the dialog.
// Remember to read values from the dialog's widgets BEFORE closing it!
UI::CloseDialog();
// Pop up a new dialog to echo the selection.
UI::OpenDialog(
`VBox(
`Label( sformat( "Selected destination directory: %1", dest_dir ) ),
`PushButton(`opt(`default), "&OK")
)
);
UI::UserInput();
UI::CloseDialog();
}
{
// Build a dialog with a tree for directory selection, three
// buttons with common values and a label that directly echoes any
// selected directory.
//
// The tree in this example uses the `notify option that makes
// UI::UserInput() return immediately as soon as the user selects a
// tree item rather than the default behaviour which waits for the
// user to activate a button.
UI::OpenDialog(
`VBox(
`MinHeight( 14,
`Tree(`id(`dest_dir),
`opt(`notify),
"Select destination directory:",
[
`item(`id(`root), "/" , true,
[
`item(`id(`etc), "etc",
[
`item("opt"),
`item("SuSEconfig"),
`item("X11")
]
),
`item("usr", false,
[
"bin",
"lib",
`item("share",
[
"man",
"info",
"emacs"
]
),
`item(`id(`usr_local),"local"),
`item("X11R6",
[
"bin",
"lib",
"share",
"man",
"etc"
]
)
]
),
`item(`id(`opt), "opt", true,
[
"kde",
"netscape",
"Office51"
]
),
`item("home"),
"work",
`item(`id(`other), "<other>")
]
)
] )
),
`HBox(
`PushButton(`id(`sel_opt ), `opt(`hstretch), "/&opt" ),
`PushButton(`id(`sel_usr ), `opt(`hstretch), "/&usr" ),
`PushButton(`id(`sel_usr_local ), `opt(`hstretch), "/usr/&local" ),
`PushButton(`id(`none ), `opt(`hstretch), "none" )
),
`HBox(
`Label("Current Value:"),
`Label(`id(`echo), `opt(`outputField, `hstretch), "?????????")
),
`PushButton(`id(`ok), `opt(`default), "&OK")
)
);
any id = nil;
repeat
{
id = UI::UserInput();
if ( id == `sel_usr ) UI::ChangeWidget(`dest_dir, `CurrentItem, "usr" );
else if ( id == `sel_usr_local ) UI::ChangeWidget(`dest_dir, `CurrentItem, `usr_local );
else if ( id == `sel_opt ) UI::ChangeWidget(`dest_dir, `CurrentItem, `opt );
else if ( id == `none ) UI::ChangeWidget(`dest_dir, `CurrentItem, nil );
UI::ChangeWidget(`echo, `Value, "");
any current_dir = UI::QueryWidget(`dest_dir, `CurrentItem);
if ( current_dir != nil )
UI::ChangeWidget(`echo, `Value, sformat( "%1", current_dir ) );
y2milestone( "Items:\n%1", UI::QueryWidget(`dest_dir, `Items ) );
y2milestone( "OpenItems: %1", UI::QueryWidget(`dest_dir, `OpenItems ) );
y2milestone( "Current Branch: %1", UI::QueryWidget(`dest_dir, `CurrentBranch ) );
} until ( id == `ok );
// Close the dialog.
// Remember to read values from the dialog's widgets BEFORE closing it!
UI::CloseDialog();
}
{
// Build a dialog with a tree for directory selection, three
// buttons with common values and a label that directly echoes any
// selected directory.
//
// The tree in this example uses the `notify option that makes
// UI::UserInput() return immediately as soon as the user selects a
// tree item rather than the default behaviour which waits for the
// user to activate a button.
UI::OpenDialog(
`MinWidth( 50,
`VBox(
`Tree(`id(`dest_dir),
`opt(`notify),
"Select destination directory:",
[
`item( "/" , true,
[
`item( "etc",
[
`item("opt"),
`item("SuSEconfig"),
`item("X11")
]
),
`item( "usr", false,
[
"bin",
"lib",
`item("share",
[
"man",
"info",
"emacs"
]
),
`item( "local" ),
`item("X11R6",
[
"bin",
"lib",
"share",
"man",
"etc"
]
)
]
),
`item( "opt", true,
[
"kde",
"netscape",
"Office51"
]
),
`item("home"),
"work",
`item( "<other>")
]
)
] ),
`HBox(
`PushButton(`id(`sel_opt), `opt(`hstretch), "/&opt" ),
`PushButton(`id(`sel_usr), `opt(`hstretch), "/&usr" ),
`PushButton(`id(`sel_usr_local), `opt(`hstretch), "/usr/&local" )
),
`HBox(
`HWeight( 2, `Label("Current Item:") ),
`HWeight( 5, `Label(`id(`echoItem), `opt(`outputField, `hstretch), "") )
),
`HBox(
`HWeight( 2, `Label("Current Branch:") ),
`HWeight( 5, `Label(`id(`echoBranch), `opt(`outputField, `hstretch), "") )
),
`HBox(
`HWeight( 2, `Label("Current Path:") ),
`HWeight( 5, `Label(`id(`echoPath), `opt(`outputField, `hstretch), "") )
),
`PushButton(`id(`ok), `opt(`default), "&OK")
)
)
);
any id = nil;
repeat
{
id = UI::UserInput();
if ( id == `sel_usr) UI::ChangeWidget( `id( `dest_dir ), `CurrentItem, "usr" );
else if ( id == `sel_usr_local) UI::ChangeWidget( `id( `dest_dir ), `CurrentItem, "local" );
else if ( id == `sel_opt) UI::ChangeWidget( `id( `dest_dir ), `CurrentItem, "opt" );
else if ( id == `dest_dir)
{
any current_dir = UI::QueryWidget(`dest_dir, `CurrentItem );
if ( current_dir != nil )
UI::ChangeWidget( `id( `echoItem ), `Value, sformat( "%1", current_dir ) );
list<string> current_branch = (list<string>) UI::QueryWidget(`dest_dir, `CurrentBranch );
if ( current_branch != nil )
{
UI::ChangeWidget(`echoBranch, `Value, sformat( "%1", current_branch) );
string current_path = mergestring( current_branch, "/" );
if ( size( current_path ) > 2 )
// Remove duplicate "/" at start
current_path = substring( current_path, 1, size( current_path )-1 );
UI::ChangeWidget(`echoPath, `Value, sformat( "%1", current_path ) );
}
}
} until ( id == `ok );
// Close the dialog.
// Remember to read values from the dialog's widgets BEFORE closing it!
UI::CloseDialog();
}
// Tree with icons
{
UI::OpenDialog(
`VBox(
`Heading( "YaST2 Mini Control Center" ),
`Tree(`id(`mod),
"Modules",
[
`item(`id( "country" ), `icon( "yast-yast-language.png" ), "Localization", true,
[
`item(`id( "keyboard" ), `icon( "yast-keyboard.png" ), "Keyboard" ),
`item(`id( "timezone" ), `icon( "yast-timezone.png" ), "Time zone" )
]
),
`item(`id( "mouse" ), `icon( "yast-mouse.png" ), "Mouse" ),
`item(`id( "lan" ), `icon( "yast-lan.png" ), "Network" ),
`item(`id( "sw_single" ), `icon( "yast-software.png" ), "Software" )
] ),
`PushButton(`id(`ok), `opt(`default), "&OK")
)
);
UI::UserInput();
UI::CloseDialog();
}
// Example showing how to replace Tree items
{
list pizza_list =
[
"Pizza Napoli",
"Pizza Funghi",
"Pizza Salami",
"Pizza Hawaii"
];
list pasta_list =
[
"Spaghetti",
"Rigatoni",
"Tortellini"
];
list veggie_toppings =
[
"Cheese",
"Mushrooms",
"Pepperoni",
"Rucola",
"Tomatoes"
];
list meat_toppings =
[
"Ham",
"Salami",
"Tuna"
];
list menu = [
`item( `id ( `pizza_branch ), "Pizza", true,
pizza_list ),
`item( `id ( `pasta_branch ), "Pasta", true,
pasta_list)
];
list toppings = [
`item( `id ( `meat_branch ), "Meat", true,
meat_toppings ),
`item( `id ( `veggie_branch ), "Veggie", true,
veggie_toppings)
];
UI::OpenDialog( `VBox(
`Tree(`id(`listing), "Daily &Specials:", menu
),
`HBox(
`PushButton(`id(`menu), "&Menu" ),
`PushButton(`id(`toppings), "&Toppings" ),
`PushButton(`id(`empty), "&None" )
),
`PushButton(`id(`ok), "&OK" )
)
);
symbol button = nil;
do
{
button = (symbol) UI::UserInput();
if ( button == `menu ) UI::ChangeWidget(`listing, `Items, menu);
if ( button == `toppings ) UI::ChangeWidget(`listing, `Items, toppings );
if ( button == `empty ) UI::ChangeWidget(`listing, `Items, [] );
} while ( button != `ok );
string order = (string) UI::QueryWidget(`listing, `CurrentItem );
UI::CloseDialog();
//
// Show the result
//
UI::OpenDialog(`VBox(
`Label( sformat( "Your order: %1", order ) ),
`PushButton(`opt(`default), "&OK" )
)
);
UI::UserInput();
UI::CloseDialog();
}
// Advanced example of using the Wizard widget.
//
// Note: YCP applications are discouraged from using the Wizard widget directly.
// Use the Wizard module instead.
{
if ( ! UI::HasSpecialWidget(`Wizard) )
{
y2error( "This works only with UIs that provide the wizard widget!" );
return;
}
string help_text =
"<p>This is a help text.</p>"
+ "<p>It should be helpful.</p>"
+ "<p>If it isn't helpful, it should rather not be called a <i>help text</i>.</p>";
UI::OpenDialog(`opt(`defaultsize ),
`Wizard(`opt(`treeEnabled),
`back, "&Back",
`abort, "Ab&ort",
`next, "&Next" ) );
// UI::DumpWidgetTree();
UI::WizardCommand(`SetDialogIcon( "/usr/share/YaST2/theme/current/icons/22x22/apps/YaST.png" ) );
UI::WizardCommand(`SetDialogHeading( "Welcome to the YaST2 installation" ) );
UI::WizardCommand(`SetHelpText( help_text ) );
UI::WizardCommand(`AddTreeItem( "", "First Toplevel Item" , "tl1" ) );
UI::WizardCommand(`AddTreeItem( "", "Second Toplevel Item", "tl2" ) );
UI::WizardCommand(`AddTreeItem( "", "Third Toplevel Item" , "tl3" ) );
UI::WizardCommand(`AddTreeItem( "tl1", "First Sublevel" , "1-1" ) );
UI::WizardCommand(`AddTreeItem( "tl1", "Second Sublevel" , "1-2" ) );
UI::WizardCommand(`AddTreeItem( "tl1", "Third Sublevel" , "1-3" ) );
UI::WizardCommand(`AddTreeItem( "tl2", "First Sublevel" , "2-1" ) );
UI::WizardCommand(`AddTreeItem( "tl2", "Second Sublevel" , "2-2" ) );
UI::WizardCommand(`AddTreeItem( "tl2", "Third Sublevel" , "2-3" ) );
UI::WizardCommand(`AddTreeItem( "1-2", "First 3rd level " , "3rd 1" ) );
UI::WizardCommand(`AddTreeItem( "1-2", "Second 3rd level " , "3rd 2" ) );
UI::WizardCommand(`AddTreeItem( "1-2", "Item without ID" , "" ) );
UI::WizardCommand(`SelectTreeItem( "3rd 1" ) );
UI::WizardCommand(`AddMenu( "&File", "file-menu" ) );
UI::WizardCommand(`AddMenu( "&Edit", "edit-menu" ) );
UI::WizardCommand(`AddMenu( "&Options", "opt-menu" ) );
UI::WizardCommand(`AddMenuEntry ( "file-menu", "&New", "file-new" ) );
UI::WizardCommand(`AddMenuEntry ( "file-menu", "&Open", "file-open" ) );
UI::WizardCommand(`AddSubMenu ( "file-menu", "Open &Recent", "file-recent" ) );
UI::WizardCommand(`AddMenuEntry ( "file-menu", "&Save", "file-save" ) );
UI::WizardCommand(`AddMenuEntry ( "file-menu", "Save &As", "file-save-as" ) );
UI::WizardCommand(`AddMenuEntry ( "file-recent", "/tmp/test1", "recent-test1" ) );
UI::WizardCommand(`AddMenuEntry ( "file-recent", "/tmp/test2", "recent-test2" ) );
UI::WizardCommand(`AddMenuEntry ( "edit-menu", "C&ut", "edit-cut" ) );
UI::WizardCommand(`AddMenuEntry ( "edit-menu", "C&opy", "edit-copy" ) );
UI::WizardCommand(`AddMenuEntry ( "edit-menu", "&Paste", "edit-paste" ) );
UI::WizardCommand(`AddMenuEntry ( "opt-menu", "&Settings", "opt-settings" ) );
UI::WizardCommand(`AddMenuSeparator ( "opt-menu" ) );
UI::WizardCommand(`AddMenuEntry ( "opt-menu", "Activate &Hypersonic Transducer", "frank-n-furter" ) );
while ( true )
{
map event = UI::WaitForEvent();
y2milestone( "Got event: %1", event );
if ( event[ "ID" ]:nil == `abort )
break;
y2milestone( "Tree selection: %1", UI::QueryWidget(`id(`wizard), `CurrentItem ) );
}
UI::CloseDialog();
}