Commit 1a026af0 authored by Steffen Michels's avatar Steffen Michels

- improve hotkey type: a hotkey is defined by exactly one key (A-Z) and...

- improve hotkey type: a hotkey is defined by exactly one key (A-Z) and optionally ctrl, alt and shift
- introduce super class for FormContainer & MessageContainer to avoid duplicate code
- interpret hotkeys on client side: append hotkey-string to menu item names & fire menu events if hotkey is pressed (only works if a form element inside the task is focused)

git-svn-id: https://svn.cs.ru.nl/repos/iTask-system/trunk@1108 63da3aa8-80fd-4f01-9db8-e6ea747a3da2
parent 7299ce5e
......@@ -36,6 +36,7 @@
,{"text":"AppletContainer.js","path":"src/js/tui/"}
,{"text":"TTCCommon.js","path":"src/js/ttc/"}
,{"text":"InteractionBase.js","path":"src/js/ttc/"}
,{"text":"FormContainer.js","path":"src/js/ttc/"}
,{"text":"FinishedContainer.js","path":"src/js/ttc/"}
,{"text":"MessageContainer.js","path":"src/js/ttc/"}
......
Ext.ns('itasks.ttc');
itasks.ttc.FormContainer = Ext.extend(Ext.Panel, {
itasks.ttc.FormContainer = Ext.extend(itasks.ttc.InteractionBase, {
initComponent : function() {
this.buildComponents(this);
this.tbar = this.content.tbar;
this.replaceItems = [];
delete this.content;
Ext.apply(this,
{ layout: 'anchor'
, taskUpdates : {}
, url: itasks.config.serverUrl + '/work/tab'
, items: [this.descpanel,this.panel]
, unstyled: true
, cls: 'FormContainer'
, autoScroll: true
});
this.cls = 'FormContainer';
itasks.ttc.FormContainer.superclass.initComponent.apply(this,arguments);
},
buildComponents: function(data){
//data.content.form.buttons = data.content.buttons;
this.panel = new itasks.ttc.form.FormPanel({
xtype: 'itasks.ttc.form.panel',
items: data.content.form,
......@@ -40,32 +25,6 @@ itasks.ttc.FormContainer = Ext.extend(Ext.Panel, {
}
},
afterRender: function(){
itasks.ttc.FormContainer.superclass.afterRender.call(this,arguments);
itasks.ttc.common.attachTaskHandlers(this);
var tb = this.getTopToolbar();
this.setupToolbar(tb);
//this.setupHotkeys(this.hotkeys);
},
addUpdate: function(name, value) {
this.taskUpdates[name] = value;
},
sendUpdates: function(delay) {
if(delay) {
new Ext.util.DelayedTask().delay(250,this.sendUpdates,this);
} else {
var wt = this.findParentByType(itasks.WorkPanel);
if(!wt) return;
wt.sendTaskUpdates(this.taskId,this.taskUpdates);
this.taskUpdates = {};
}
},
update: function(data) {
if(data.updates) {
var num = data.updates.length;
......@@ -226,77 +185,6 @@ itasks.ttc.FormContainer = Ext.extend(Ext.Panel, {
if(newTb)
tb.add(newTb);
this.setupToolbar(tb);
},
setupToolbar: function(tb) {
tb.setVisible(tb.items.length > 0);
var enabledPresent = false;
for(var i = 0; i < tb.items.length; i++)
if(!tb.get(i).disabled) {
enabledPresent = true;
break;
}
var cls = 'ToolbarNoEnabledItems';
if(enabledPresent)
tb.removeClass(cls);
else {
tb.removeClass(cls);
tb.addClass(cls);
}
var checkGroupOnly = function(item) {
if(item.disabled)
return true;
if(Ext.isDefined(item.topGroupAction)) {
return item.topGroupAction;
} else if (item.getXType() != 'menuseparator') {
var children = item.items || item.menu.items;
for(var i = 0; i < children.length; i++) {
if (!checkGroupOnly(children.get(i)))
return false;
}
}
return true;
};
var cls = 'ToolbarGroupActionsOnly';
if(checkGroupOnly(tb)) {
tb.removeClass(cls);
tb.addClass(cls);
} else {
tb.removeClass(cls);
}
},
setupHotkeys: function(hotkeys) {
if (this.keyMap)
this.keyMap.disable();
if (hotkeys.length == 0)
return;
var conf = new Array();
var form = this;
for (i = 0; i < hotkeys.length; i++) {
var h = hotkeys[i][1];
conf[conf.length] = {
key: h.keys,
ctrl: h.ctrl,
alt: h.alt,
shift: h.shift,
stopEvent: true,
handler: function() {
form.addUpdate('hotkey', this);
form.sendUpdates();
},
scope: hotkeys[i][0]
};
}
this.keyMap = new Ext.KeyMap(this.getEl(), conf);
}
});
......
......@@ -278,6 +278,7 @@ itasks.ttc.GroupContainer = Ext.extend(Ext.Panel,{
});
});
itasks.ttc.common.attachTaskHandlers(groupTbar,this.taskId);
itasks.ttc.common.setupHotkeys(groupTbar, this);
groupTbar.doLayout();
},
......
Ext.ns('itasks.ttc');
itasks.ttc.InteractionBase = Ext.extend(Ext.Panel, {
initComponent : function() {
this.buildComponents(this);
this.tbar = this.content.tbar;
delete this.content;
Ext.apply(this,
{ layout: 'anchor'
, taskUpdates : {}
, url: itasks.config.serverUrl + '/work/tab'
, items: [this.descpanel,this.panel]
, unstyled: true
, autoScroll: true
});
itasks.ttc.InteractionBase.superclass.initComponent.apply(this,arguments);
},
afterRender: function(){
itasks.ttc.InteractionBase.superclass.afterRender.call(this,arguments);
//attachTaskHandlers is moved to file 'TTCCommon.js'
itasks.ttc.common.attachTaskHandlers(this);
var tb = this.getTopToolbar();
this.setupToolbar(tb);
//this.setupHotkeys(this.hotkeys);
},
addUpdate: function(name, value) {
this.taskUpdates[name] = value;
},
sendUpdates: function(delay) {
if(delay) {
new Ext.util.DelayedTask().delay(250,this.sendUpdates,this);
} else {
var wt = this.findParentByType(itasks.WorkPanel) || this.workPanel;
if(!wt) return;
wt.sendTaskUpdates(this.taskId,this.taskUpdates);
this.taskUpdates = {};
}
},
setupToolbar: function(tb) {
tb.setVisible(tb.items.length > 0);
var enabledPresent = false;
for(var i = 0; i < tb.items.length; i++)
if(!tb.get(i).disabled) {
enabledPresent = true;
break;
}
var cls = 'ToolbarNoEnabledItems';
if(enabledPresent)
tb.removeClass(cls);
else {
tb.removeClass(cls);
tb.addClass(cls);
}
var checkGroupOnly = function(item) {
if(item.disabled)
return true;
if(Ext.isDefined(item.topGroupAction)) {
return item.topGroupAction;
} else if (item.getXType() != 'menuseparator') {
var children = item.items || item.menu.items;
for(var i = 0; i < children.length; i++) {
if (!checkGroupOnly(children.get(i)))
return false;
}
}
return true;
};
var cls = 'ToolbarGroupActionsOnly';
if(checkGroupOnly(tb)) {
tb.removeClass(cls);
tb.addClass(cls);
} else {
tb.removeClass(cls);
}
itasks.ttc.common.setupHotkeys(tb, this);
}
});
\ No newline at end of file
Ext.ns('itasks.ttc');
itasks.ttc.MessageContainer = Ext.extend(Ext.Panel, {
itasks.ttc.MessageContainer = Ext.extend(itasks.ttc.InteractionBase, {
initComponent : function() {
this.buildComponents(this);
this.tbar = this.content.tbar;
delete this.content;
Ext.apply(this,
{ layout: 'anchor'
, taskUpdates : {}
, url: itasks.config.serverUrl + '/work/tab'
, items: [this.descpanel,this.panel]
, unstyled: true
, autoScroll: true
, cls: 'MessageContainer'
});
this.cls = 'MessageContainer';
itasks.ttc.MessageContainer.superclass.initComponent.apply(this,arguments);
},
buildComponents: function(data){
data.content.form.buttons = data.content.buttons;
this.panel = {
xtype: 'itasks.ttc.message.panel',
items: data.content.form,
......@@ -41,104 +24,6 @@ itasks.ttc.MessageContainer = Ext.extend(Ext.Panel, {
}
},
afterRender: function(){
itasks.ttc.MessageContainer.superclass.afterRender.call(this,arguments);
//attachTaskHandlers is moved to file 'TTCCommon.js'
itasks.ttc.common.attachTaskHandlers(this);
var tb = this.getTopToolbar();
this.setupToolbar(tb);
//this.setupHotkeys(this.hotkeys);
},
addUpdate: function(name, value) {
this.taskUpdates[name] = value;
},
sendUpdates: function(delay) {
if(delay) {
new Ext.util.DelayedTask().delay(250,this.sendUpdates,this);
} else {
var wt = this.findParentByType(itasks.WorkPanel) || this.workPanel;
if(!wt) return;
wt.sendTaskUpdates(this.taskId,this.taskUpdates);
this.taskUpdates = {};
}
},
setupToolbar: function(tb) {
tb.setVisible(tb.items.length > 0);
var enabledPresent = false;
for(var i = 0; i < tb.items.length; i++)
if(!tb.get(i).disabled) {
enabledPresent = true;
break;
}
var cls = 'ToolbarNoEnabledItems';
if(enabledPresent)
tb.removeClass(cls);
else {
tb.removeClass(cls);
tb.addClass(cls);
}
var checkGroupOnly = function(item) {
if(item.disabled)
return true;
if(Ext.isDefined(item.topGroupAction)) {
return item.topGroupAction;
} else if (item.getXType() != 'menuseparator') {
var children = item.items || item.menu.items;
for(var i = 0; i < children.length; i++) {
if (!checkGroupOnly(children.get(i)))
return false;
}
}
return true;
};
var cls = 'ToolbarGroupActionsOnly';
if(checkGroupOnly(tb)) {
tb.removeClass(cls);
tb.addClass(cls);
} else {
tb.removeClass(cls);
}
},
setupHotkeys: function(hotkeys) {
if (this.keyMap)
this.keyMap.disable();
if (hotkeys.length == 0)
return;
var conf = new Array();
var form = this;
for (i = 0; i < hotkeys.length; i++) {
var h = hotkeys[i][1];
conf[conf.length] = {
key: h.keys,
ctrl: h.ctrl,
alt: h.alt,
shift: h.shift,
stopEvent: true,
handler: function() {
form.addUpdate('hotkey', this);
form.sendUpdates();
},
scope: hotkeys[i][0]
};
}
this.keyMap = new Ext.KeyMap(this.getEl(), conf);
},
update: function(data){
//this.setupHotkeys(data.hotkeys);
}
......
......@@ -178,4 +178,59 @@ itasks.ttc.common.setFullscreen = function(ct){
ct.fullscreen = true;
};
itasks.ttc.common.setupHotkeys = function(tb, parent) {
// collect all hotkeys
var hotkeys = new Ext.util.MixedCollection();
var collectHotkeys = function(item) {
var hk = item.hotkey;
if(hk) {
hotkeys.add({hotkey: hk, menuitem: item});
// append hotkey-string to item name
var str = hk.key;
if (hk.shift) str = "Shift+" + str;
if (hk.alt) str = "Alt+" + str;
if (hk.ctrl) str = "Ctrl+" + str;
item.setText(item.text + "&nbsp;&nbsp;&nbsp;&nbsp;(" + str + ")");
} else if (item.items || item.menu) {
var children = item.items || item.menu.items;
for(var i = 0; i < children.length; i++) {
collectHotkeys(children.get(i));
}
}
}
collectHotkeys(tb);
// disabled old hotkeys
if (parent.keyMap)
parent.keyMap.disable();
if (hotkeys.getCount() == 0)
return;
// build up config array for KeyMap
var conf = new Array();
hotkeys.each(function(hk) {
var h = hk.hotkey;
conf[conf.length] = {
key: h.key,
ctrl: h.ctrl,
alt: h.alt,
shift: h.shift,
stopEvent: true,
handler: function() {
var item = hk.menuitem;
// only fire hotkey-event if item is not disabled
if (!item.disabled) {
parent.addUpdate(item.name, item.value);
parent.sendUpdates();
}
}
};
});
parent.keyMap = new Ext.KeyMap(parent.getEl(), conf);
};
Ext.reg('itasks.ttc.common.description', itasks.ttc.common.DescriptionPanel);
\ No newline at end of file
......@@ -92,6 +92,7 @@
<script type="text/javascript" src="../js/ttc/ParallelContainer.js"></script>
<script type="text/javascript" src="../js/ttc/FinishedContainer.js"></script>
<script type="text/javascript" src="../js/ttc/InteractionBase.js"></script>
<script type="text/javascript" src="../js/ttc/FormContainer.js"></script>
<script type="text/javascript" src="../js/ttc/InstructionContainer.js"></script>
<script type="text/javascript" src="../js/ttc/MessageContainer.js"></script>
......
......@@ -9,19 +9,19 @@ textEditor = [workflow "Examples/Miscellaneous/Text Editor" (textEditorApp <<@ S
textEditorApp :: Task Void
textEditorApp =
setMenus
[ Menu "File" [ MenuItem "New" ActionNew (hotkey 'n')
, MenuItem "Open..." ActionOpen (hotkey 'o')
[ Menu "File" [ MenuItem "New" ActionNew (hotkey N)
, MenuItem "Open..." ActionOpen (hotkey O)
, MenuName recOpenedMenu (SubMenu "Recently Opened" [])
, MenuSeparator
, MenuItem "Save" ActionSave (hotkey 's')
, MenuItem "Save As..." ActionSaveAs (hotkey 'a')
, MenuItem "Save" ActionSave (hotkey S)
, MenuItem "Save As..." ActionSaveAs (hotkey A)
, MenuSeparator
, MenuItem "Close" ActionClose (hotkey 'c')
, MenuItem "Quit" ActionQuit (hotkey 'q')
, MenuItem "Close" ActionClose (hotkey C)
, MenuItem "Quit" ActionQuit (hotkey Q)
]
, Menu "Edit" [ MenuItem "Replace..." ActionReplace (hotkey 'r')
, Menu "Edit" [ MenuItem "Replace..." ActionReplace (hotkey R)
]
, Menu "Tools" [ MenuItem "Statistics..." ActionStats (hotkey 't')
, Menu "Tools" [ MenuItem "Statistics..." ActionStats (hotkey T)
]
, Menu "Help" [ MenuItem "About" ActionShowAbout Nothing
]
......@@ -37,8 +37,8 @@ where
, GroupAction ActionQuit (GExtend [quit iterateEditors <<@ GBModal]) GroupAlways
]
hotkey :: !Char -> Maybe Hotkey
hotkey key = Just {ctrl = True, alt = False, shift = True, keys = toString key}
hotkey :: !Key -> Maybe Hotkey
hotkey key = Just {ctrl = True, alt = False, shift = True, key = key}
ActionReplace :== ActionLabel "replace"
ActionStats :== ActionLabel "stats"
......
definition module MenuTasks
from ProcessDB import ::Menu, ::MenuItem, ::Hotkey{..}
from ProcessDB import ::Menu, ::MenuItem, ::Hotkey{..}, ::Key(..)
from TSt import ::Task
from Void import :: Void
import StdMaybe, GenPrint, GenParse, GenVisualize, GenUpdate
......
implementation module MenuTasks
from ProcessDB import ::Menu(..), ::MenuItem(..), ::Hotkey, ::Process{..}
from ProcessDB import ::Menu(..), ::MenuItem(..), ::Hotkey, ::Key, ::Process{..}
from TSt import ::Task
from Void import :: Void
import TSt, CoreCombinators, StdMisc
import StdList
derive class iTask Menu, MenuItem, Hotkey
derive class iTask Menu, MenuItem, Hotkey, Key
derive bimap (,), Maybe
getMenus :: Task (Maybe [Menu])
......
......@@ -52,11 +52,13 @@ getActionIcon :: !Action -> String
| MenuSeparator
| MenuName !String !MenuItem
:: Hotkey = { keys :: !String
:: Hotkey = { key :: !Key
, ctrl :: !Bool
, alt :: !Bool
, shift :: !Bool
}
:: Key = A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
class ProcessDB st
where
......
......@@ -10,7 +10,7 @@ import TaskPanel
derive bimap (,), Maybe
derive JSONEncode Menu, MenuItem, Hotkey, Action, User, UserDetails, Password
derive JSONEncode Menu, MenuItem, Hotkey, Key, Action, User, UserDetails, Password
derive JSONDecode ManagerProperties, TaskPriority, User, UserDetails, Password
JSONEncode{|Timestamp|} (Timestamp x) = JSONEncode{|*|} x
......
......@@ -2,14 +2,14 @@ implementation module TUIDefinition
import JSON,StdList,StdBool,GenEq
from Types import :: Document, :: DocumentId
from ProcessDB import :: Hotkey
from ProcessDB import :: Hotkey, :: Key
//derive gEq TUIDef, TUIBasicControl, TUICurrencyControl, TUIDocumentControl, TUIConstructorControl, TUIButtonControl, TUIListItemControl
//derive gEq TUITupleContainer, TUIRecordContainer, TUIListContainer, JSONNode, Maybe, Document
//derive gEq TUIButton, TUIUpdate, TUIChoiceControl, TUIMenuButton, TUIMenu, TUIMenuItem, TUIHtmlPanel, Hotkey
//JSON Encoding of TUI definitions is directly encoded as JSON data.
derive JSONEncode TUIButton, TUIUpdate, TUIMenuButton, TUIMenu, TUIMenuItem, TUIHtmlPanel, Hotkey
derive JSONEncode TUIButton, TUIUpdate, TUIMenuButton, TUIMenu, TUIMenuItem, TUIHtmlPanel, Hotkey, Key
derive JSONEncode TUIBasicControl, TUICurrencyControl, TUIDocumentControl, TUIConstructorControl
derive JSONEncode TUIButtonControl, TUIListItemControl, TUIChoiceControl
derive JSONEncode TUITupleContainer, TUIRecordContainer, TUIListContainer
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment