#!html
This Plugin is not part of the current lcd4linux. This wiki entry was created for discussion about a plugin prototype.
====== Plugin Menu ======
\\\
{{:/raw-attachment/wiki/FutabaVFD/FutabaVFD_Menu_Serial.png}}
\\\
Plugin Menu, [[:plugin_control_ctrl_serial|ctrl_serial]] (2 LED, 4 Buttons), [[:FutabaVFD|Futaba VFD]]
\\\
\\\
====== 1. Description ======
The plugin Menu allows to configure and use menus.
This Plugin uses [[:plugin_layout|plugin Layout]] to switch Layouts. Therefore **plugin Layout has to be configured and activated too**.\\\
Due to this and the similar use of actions it's highly recommended to read the wiki entry of [[:plugin_layout|plugin Layout]] first.
While simple menus can be create with plugin Layout by using the change of button functions depending on the current layout, with plugin Menu menus are builded by using
item selection and button functions that are depending on the current active item. So more choises can be made on a small display or with very few buttons.
Its also a more natural way to build menus if controls with arrow buttons (e.g. [[:widget_keypad|widget Keypad]]) are used.
A part of a menu configured in the attached sample config file looks like this:
┌────────────────────────────────────────┐
│1.1/1.2 Main Menu 1/2 │
│ [[:SET|VALUE]] >CPU< [[:RAM]] [[:DISK]] [[:ETH]] >>│
└────────────────────────────────────────┘
In this example //CPU// is setected, inactive items are marked with //[[:...]]// to distinguish between selectable items and normal text like the title (//Main Menu...//).
**The plugin Menu does NOT draw any item or menu itself.** It only stores the current menu and item internally and provides functions to switch the current menu or item and functions to detect if a menu/item is active. Latter functions are used by visible widgets (usually the [[:widget_text|Text widget]]) to make the menu state visible (e.g. marks or blink). The functionality of the menus is specified by setting actions for each item.
By this its very adjustable how a menu look and acts but that is paid with a more complex configuration.
\\\
\\\
====== 2. Configuration ======
===== 2.1. Structure
=====
The structure in the config-file looks like this:
Plugin Menu {
active 1
blink 150
initmenu 'M_Menu'
confirm layout::confirm()
cancel layout::cancel()
left layout::left()
right layout::right()
Menu1 {
name 'M_Menu'
layoutgroup 'G_Menu'
confirm menu::none(); layout::layout('G_Diff','L_Dummy')
cancel ' '
left menu::prev(0)
right menu::next(0)
Item1 {
name 'I_Menu_SET'
layout 'L_Menu_1'
confirm menu::menu('M_SET')
}
Item2 {
name 'I_Menu_CPU'
confirm menu::none(); layout::layout('G_Diff','L_CPU')
}
...
}
Menu2 {
...
}
}
...
Widget W_Menu_CPU {
class 'text'
update t_item
width 5
expression menu::if('M_Menu','I_Menu_CPU','>CPU<','[[:CPU]]')
}
...
"active 1" activates the plugin. If it has not been activated all of its functions will return -1.
The plugin must contain at least 1 menu - each menu at least 1 item. Menus and items have to be numbered serially starting with 1.
In the example a text widget linked to item //I_Menu_CPU// of menu //M_Menu// is shown. If this item is active, the widget will be drawn as //>CPU/, else //[[:CPU]]//
Of course //menu::if()// could be used in the prefix and postfix expressions of the text widget too.
Widgets which are linked to a menu item that way have to poll the current menu state periodically. Using a variable to set the //update// parameter of the widget to a decisecond might be a good idea.
For each menu a [[:plugin_layout|layout group]] has to be set. For each menu the [[:plugin_layout|layout]] of the first item has to be set explicitly. For all other items it's assumed that it has the same layout as its previous item if no layout was set. Of course this layout should contain the widget which represents the item visually.
If the current menu/item is switched the plugin will switch to the layout which was set for this item automatically.
===== 2.2. Section Plugin Menu
=====
**Parameters:**
|**active**|Set to 1 to activate plugin, otherwise the plugin won't work and all of its functions return -1. |
|**initmenu***|Name of the menu that should be active initially (first item). (If not set: No menu/item is active at the start.)|
|{{anchor:blink}}**blink***|Duration in milliseconds between a state change in function menu::blink(...); default: 200|
|*****|see [[:#actions|Actions]]|
| |* //optional//|
**Subsections**
|**Menus**|see [[:#menu|Subsection Menu]]|
===== [[:======#menu]]2.3. Subsection Menu
**Parameters:**
|**name***|Unique name of the menu. Default: 'Menu1' for Menu1, ...|
|**layoutgroup**|Layout group of the menu.|
|*****|see [[:#actions|Actions]]|
| |* //optional//|
**Subsections**
|**Items**|see [[:#item|Subsection Item]]|
===== [[:======#item]]2.4. Subsection Item
**Parameters:**
|**name**|Name of the item. Default: 'Item1' for Item1, ...|
|**layout****|Layout of the item.|
|*****|see [[:#actions|Actions]]|
| |* //optional, ** sometimes optional //|
===== [[:======#actions]]2.5. Actions
Actions are parameters whose values are expressions which will be evaluated when the action is triggered.
\\\
\\\
These actions can be used to modify the function of button/timer events dependent on the current item/menu.
|**onenter***|Triggered shortly after enter new item/menu.|
|**onexit***|Triggered shortly before exit new item/menu.|
|**confirm**|Triggered by [[:#functions|menu::confirm()]]|
|**cancel**|Triggered by [[:#functions|menu::cancel()]]|
|**up**|Triggered by [[:#functions|menu::up()]]|
|**down**|Triggered by [[:#functions|menu::down()]]|
|**left**|Triggered by [[:#functions|menu::left()]]|
|**right**|Triggered by [[:#functions|menu::right()]]|
|**action1**|Triggered by [[:#functions|menu::trigger('action1')]]|
|...| |
|**action 20**|Triggered by [[:#functions|menu::trigger('action20')]]|
| |* only in subsection Menu and Item|
The actions "onenter" and "onexit" will be triggered by a change of the current active item.
This may result in switching the current layout. In this case onexit/onenter of plugin Layout will be evaluated too.
Sequence:
1. onexit of old item
1. onexit of old menu
1. switch menu/item
1. onexit of old layout
1. onexit of old group
1. switch group/layout
1. onenter of new group
1. onenter of new layout
1. onenter of new menu
1. onenter of new item
The other actions will be triggered directly by homonymic functions in the following way.
item level ==> menu level ==> plugin level (First matching action found will be triggered.)
For instance the //confirm// actions in the example above:
Usually the //confirm// action is defined for each item to switch to a new layout or another menu, if it's triggered by //menu::confirm()//.
Since some items in the samples are dummies these items will have no //confirm// action, because it can be set as default for Menu1 (M_Menu).
After this confirm action is evaluated no menu will be set because of //menu::none()//. Now a call of menu::confirm() is handled on plugin level and
gets redirected to a call of layout::confirm(). This redirection on plugin level is useful because menus will often lead to layouts
without a active menu but the need to react on buttons - especially //cancel// to get back to the menu.
====== [[:======#functions]]3. Functions =
\\\
All functions return -1 on error (e.g. plugin not active, wrong config, wrong indexes, name not found).
\\\
|**Function**|**Args**|**Description**|
| || **Item info/switch** |
|**menu::item(** //[[:[menu]],item]// **)**| 0-2 |switches to item or get current item index of the current menu. [[:#fct_item|See below.]]|
|**menu::items()**| 0 |returns number of items in current menu|
|**menu::itemname()**| 0 |returns name of the current item|
|**menu::prev(** //loop// **)**| 1 |switch to previous item of current menu - no change if first item of menu and loop == 0, returns -1 on error, else 0 |
|**menu::next(** //loop// **)**| 1 |switch to next item of current menu - no change if last item of menu and loop == 0, returns -1 on error, else 0 |
| || **Menu info/switch** |
|**menu::menu(** //[[:menu]]// **)**| 0-1 |returns menu index or switches to last active item of //menu//. [[:#fct_menu|See below.]]|
|**menu::menus()**| 0 |returns number of menus|
|**menu::menuname()**| 0 |returns name of current menu|
|**menu::none()**| 0 |No menu is active after this function was called.|
| || **Functions to use in widget expressions** |
|**menu::if(** //menu, item, ifVal, elseVal// **)**| 4 |returns //ifVal// if //item// of //menu// is active, else //elseVal//. //menu// & //item// may be the names or the indexes.|
|**menu::blink(** //menu, item, val, [[:elseVal]]// **)**| 3-4 |Return 'blinking text'. //menu// & //item// may be the names or the indexes. [[:#fct_blink|See below.]]|
|**menu::check(** //menu, item// **)**| 2 |returns 1 if //item// of //menu// is active, else 0. //menu// & //item// may be the names or the indexes.|
| || **Action trigger** |
|**menu::trigger(** arg **)**| 1 |See [[:#actions|Actions]], Examples for value of arg: '' 'action17', 'confirm' '', returns 1 if action found, else 0|
|**menu::confirm()**| 0 |See [[:#actions|Actions]], returns 1 if action found, else 0|
|**menu::cancel()**| 0 |See [[:#actions|Actions]], returns 1 if action found, else 0|
|**menu::left()**| 0 |See [[:#actions|Actions]], returns 1 if action found, else 0|
|**menu::right()**| 0 |See [[:#actions|Actions]], returns 1 if action found, else 0|
|**menu::up()**| 0 |See [[:#actions|Actions]], returns 1 if action found, else 0|
|**menu::down()**| 0 |See [[:#actions|Actions]], returns 1 if action found, else 0|
\\\
{{anchor:fct_item]**menu::item(** //[[:[menu]],item}}// **)**
\\\
|menu::item()|returns index of current item within current menu or 0 if no menu is active|
|menu::item(//item//)|switch to //item// within current menu, //item// may be the name or the index of the item, returns -1 on error, else 0|
|menu::item(//menu//,//item//)|switch to //item// of //menu//, //menu// & //item// may be the names or the indexes, returns -1 on error, else 0|
\\\
{{anchor:fct_menu]**menu::menu(** //[menu}}// **)**
\\\
|menu::menu()|returns index of current menu or 0 if no menu is active|
|menu::menu(//menu//)|switch to the last active item (default: first) of //menu//. //menu// may be the name or the index of the menu, returns -1 on error, else 0|
\\\
{{anchor:fct_blink]**menu::blink(** //menu, item, val, [elseVal}}// **)**
\\\
If //item// of //menu// is active, //val// or a blank string of the same length are returned. This value alternates [[:#blink|//blink//]] milliseconds what results in blinking.
If //item// of //menu// is not active, //elseVal// will be return if set, a non blinking //val// otherwise.
Note: Since the update frequency of the widget which calls menu::blink(...) an the frequency of the blinking effect will interfere, it is '//highly recommended to set each of the update parameter of these widget to be a proper divisor of the value of [[:#blink|//blink//]]//'.
====== 4. Example ======
Download: [[::raw-attachment:wiki:plugin_menu:sample_menu.conf|sample_menu.conf]]
The example is written for the [[:Curses|Curses driver]]. So no real LCD is needed for testing. It also allows the use of the keyboard to switch layouts.
(If the real display has no keypad the [[:plugin_control|Control plugin]] may be used to connect buttons to lcd4linux.)
Use [[:Manual#USAGE|option -f]] to activate these configuration files.
The indexes of the group, layout, menu and item are displayed in the upper left corner of all layouts to make it easy to find the matching part in the config file.
**Control**
The example can be controlled with the 4 arrow keys of the keyboard using the keypad. This is already configured in the sample.
Beside that other controllers could be used to. A sample configuration of plugin [[:plugin_control|Control]] is included but the plugin is set to inactive, since there would be parameters to set and controllers to install before the plugin would work.
(Note: The [[:Curses|Curses driver]] will react on mouse wheel movements since the mouse has not been detached from the kernel by the [[:plugin_control_ctrl_usbmouse|usb mouse controller]].)
|**Keypad**|**Function**|**ctrl_fifo***|**ctrl_usbmouse***|**ctrl_serial***|
|up|confirm / enter or leave edit mode|send "o" (ok) to fifo|left mouse button|button S2|
|down|back to main menu|send "c" to fifo|right mouse button|button S2|
|left|previous item / value|send "l" to fifo|scroll up|button S1|
|right|next item / value|send "r" to fifo|scroll down|button S0|
| || ||*by plugin [[:plugin_control|Control]]|
**Main menu**
The example will start on the first of two sites of the main menu. Except the first 2 items all other items are dummies du demonstrate item switching.
On the first site of the main menu uses uniform style for marking the active item. On the second site some other possibilies (e.g. blinking) are shown.
Items are switched by the arrow keys left and right. All of this items act as buttons. To confirm (press one of these virtual buttons) arrow key up is used.
Pressing arrow key down (cancel) will have no effect in the main menu. Outside the main menu cancel will return to it.
**Set value**
This is the first item of the main menu. It leads to a new layout and menu. This menu contains two items. These items act as combo boxes.
In normal mode items are switched by the arrow keys left and right. By pressing arrow key up the current item is set to edit mode. Now the value of the combo box (indeed the value of a variable modified by [[:#actions|actions]]) can be switched by the arrow keys left and right. The edit mode can be left by pressing arrow key up again.
(Note: The [[:plugin_list|List plugin]] is used to hold/select the switchable values.)
**CPU**
This is the second item of the main menu. It leads to a dummy with some bars. Pressing arrow key down (cancel) will return to the main menu.
**Generic dummy**
All except the first two items of the main menu will lead to this view with a scrolling text. Pressing arrow key down (cancel) will return to the main menu.