added support for item shortcuts in browse...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2017-01-27 17:33:21 +03:00
parent 7580a3f57b
commit b79ef79242
6 changed files with 172 additions and 79 deletions

View File

@ -164,20 +164,25 @@ body {
}
.browse-widget.cloud-view .list>div {
.browse-widget .list .keyboard-shortcut {
text-decoration: underline;
}
.browse-widget.cloud-view .list .item {
font-size: small;
}
/* browse pinned items... */
.browse-widget .list>div:not(.pinned) .pin-set {
.browse-widget .list .item:not(.pinned) .pin-set {
display: none;
}
.browse-widget .list>div.pinned .pin-unset {
.browse-widget .list .item.pinned .pin-unset {
display: none;
}
/*
.browse-widget .list>div.pinned + :not(.pinned) {
.browse-widget .list .item.pinned + :not(.pinned) {
border-top: solid 1px rgba(255, 255, 255, 0.3);
}
*/
@ -189,12 +194,12 @@ body {
/* Metadata viewer */
.item-value-view .text:first-child,
.browse-widget.metadata-view .list>div .text:first-child {
.browse-widget.metadata-view .list .item .text:first-child {
width: 50%;
font-weight: bold;
}
.item-value-view .text:nth-child(2),
.browse-widget.metadata-view .list>div .text:nth-child(2) {
.browse-widget.metadata-view .list .item .text:nth-child(2) {
font-style: italic;
-moz-user-select: auto;
@ -208,36 +213,36 @@ body {
/* External Editor List */
.browse-widget.editor-list .list>div:first-child .text:after {
.browse-widget.editor-list .list .item:first-child .text:after {
content: "(primary)";
margin-left: 5px;
opacity: 0.5;
font-style: italic;
}
/* XXX this is ugly -- use a class... */
.browse-widget.editor-list .list>div:first-child .button:nth-child(4) {
.browse-widget.editor-list .list .item:first-child .button:nth-child(4) {
opacity: 0.1;
}
.browse-widget.editor-list .list>div:nth-child(2):not(:last-child) .text:after {
.browse-widget.editor-list .list .item:nth-child(2):not(:last-child) .text:after {
content: "(secondary)";
margin-left: 5px;
opacity: 0.5;
font-style: italic;
}
/* XXX this is ugly -- use a class... */
.browse-widget.editor-list .list>div:nth-child(2) .button:nth-child(3) {
.browse-widget.editor-list .list .item:nth-child(2) .button:nth-child(3) {
opacity: 0.1;
}
/* slideshow interval list... */
.browse-widget.tail-action .list>div:last-child {
.browse-widget.tail-action .list .item:last-child {
margin-top: 0.2em;
border-top: solid 1px rgba(255,255,255, 0.2);
}
.browse-widget.tail-action .list>div:last-child .text {
.browse-widget.tail-action .list .item:last-child .text {
font-style: italic;
}
.browse-widget.tail-action .list>div:last-child .button {
.browse-widget.tail-action .list .item:last-child .button {
display: none;
}
@ -274,7 +279,7 @@ body {
*/
/* XXX experimental key mappings... */
.browse-widget.browse-actions.show-keys .list>div:after {
.browse-widget.browse-actions.show-keys .list .item:after {
display: inline;
position: relative;
content: attr(keys);
@ -286,14 +291,14 @@ body {
opacity: 0.3;
font-style: italic;
}
.browse-widget.browse-actions.show-keys .list>div.disabled:after {
.browse-widget.browse-actions.show-keys .list .item.disabled:after {
opacity: 0.5;
}
.browse-widget.browse-actions.show-keys .list>div:hover:after {
.browse-widget.browse-actions.show-keys .list .item:hover:after {
opacity: 0.5;
}
.browse-widget.browse-actions.show-keys .list>div.disabled:hover:after {
.browse-widget.browse-actions.show-keys .list .item.disabled:hover:after {
opacity: 1;
}
@ -305,10 +310,10 @@ body {
/* key binding editor... */
.browse-widget.key-bindings .list>div:not(.selected):not(.mode):nth-child(even) {
.browse-widget.key-bindings .list .item:not(.selected):not(.mode):nth-child(even) {
background: rgba(0, 0, 0, 0.03);
}
.browse-widget.key-bindings .list>div .button {
.browse-widget.key-bindings .list .item .button {
background-color: rgba(0, 0, 0, 0.1);
}
@ -324,7 +329,7 @@ body {
font-style: italic;
}
.browse-widget.key-bindings .list>div .text:not(:first-child) {
.browse-widget.key-bindings .list .item .text:not(:first-child) {
display: inline;
position: relative;
@ -336,7 +341,7 @@ body {
font-style: italic;
}
/* NOTE: the last element is a space... */
.browse-widget.key-bindings.browse .list>div .text:last-child {
.browse-widget.key-bindings.browse .list .item .text:last-child {
margin-right: 0em;
}
@ -367,7 +372,7 @@ body {
/* dark theme... */
.dark .browse-widget.key-bindings .list>div:not(.selected):not(.mode):nth-child(even) {
.dark .browse-widget.key-bindings .list .item:not(.selected):not(.mode):nth-child(even) {
background: rgba(255, 255, 255, 0.03);
}

View File

@ -215,7 +215,7 @@
opacity: 0.9;
background-color: rgba(0, 0, 0, 0.2);
}
.browse-widget .list>div {
.browse-widget .list .item {
padding: 5px;
padding-left: 15px;
padding-right: 15px;
@ -233,7 +233,7 @@
/*white-space: nowrap;*/
overflow: hidden;
}
.browse-widget .list>div[count]:after {
.browse-widget .list .item[count]:after {
display: inline-block;
content: "(" attr(count) ")";
@ -245,39 +245,39 @@
opacity: 0.4;
}
/* highlight seach... */
.browse-widget .list>div .text b {
.browse-widget .list .item .text b {
background-color: rgba(0, 0, 255, 0.5);
}
.browse-widget .list>div.strike-out .text {
.browse-widget .list .item.strike-out .text {
text-decoration: line-through;
opacity: 0.3;
}
.browse-widget .list>div.highlighted {
.browse-widget .list .item.highlighted {
font-style: italic;
font-weight: bold;
}
.browse-widget .list>div.highlighted .text:last-child:after {
.browse-widget .list .item.highlighted .text:last-child:after {
content: ' *';
}
.browse-widget:not(.flat) .list div:not(.not-traversable) .text:after {
content: "/";
}
.browse-widget .list>div.selected:focus,
.browse-widget .list>div.selected :focus,
.browse-widget:focus .list>div.selected,
.browse-widget .list .item.selected:focus,
.browse-widget .list .item.selected :focus,
.browse-widget:focus .list .item.selected,
.browse-widget .path>.dir:hover,
.browse-widget .list>div:hover {
.browse-widget .list .item:hover {
background: rgba(0, 0, 0, 0.05);
opacity: 0.9;
}
.browse-widget .list>div.selected {
.browse-widget .list .item.selected {
background: rgba(0, 0, 0, 0.08);
}
.browse-widget .list>div.selected:focus,
.browse-widget .list>div.selected :focus,
.browse-widget:focus .list>div.selected {
.browse-widget .list .item.selected:focus,
.browse-widget .list .item.selected :focus,
.browse-widget:focus .list .item.selected {
background: rgba(0, 0, 0, 0.08);
box-shadow: rgba(0, 0, 0, 0.2) 0.1em 0.1em 0.2em;
@ -285,16 +285,16 @@
}
/* XXX need to make the next two different... */
.browse-widget .list>div.filtered-out {
.browse-widget .list .item.filtered-out {
opacity: 0.5;
}
.browse-widget:not(.show-filtered-out) .list>div.filtered-out {
.browse-widget:not(.show-filtered-out) .list .item.filtered-out {
display: none;
}
.browse-widget .list>div.disabled {
.browse-widget .list .item.disabled {
opacity: 0.3;
}
.browse-widget .list>div.hidden {
.browse-widget .list .item.hidden {
font-style: italic;
}
@ -306,42 +306,45 @@
float: left;
font-size: small;
}
.browse-widget.filtering .list>div .text:first-child:before {
.browse-widget.filtering .list .item .text:first-child:before {
display: none;
}
.browse-widget .list>div .text:first-child:before {
.browse-widget .list .item .text:first-child:before {
width: 12px;
margin-left: -15px;
}
.browse-widget:not(.no-item-numbers) .list>div:nth-of-type(1) .text:first-child:before {
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(1) .text:first-child:before {
content: "1";
}
.browse-widget:not(.no-item-numbers) .list>div:nth-of-type(2) .text:first-child:before {
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(2) .text:first-child:before {
content: "2";
}
.browse-widget:not(.no-item-numbers) .list>div:nth-of-type(3) .text:first-child:before {
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(3) .text:first-child:before {
content: "3";
}
.browse-widget:not(.no-item-numbers) .list>div:nth-of-type(4) .text:first-child:before {
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(4) .text:first-child:before {
content: "4";
}
.browse-widget:not(.no-item-numbers) .list>div:nth-of-type(5) .text:first-child:before {
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(5) .text:first-child:before {
content: "5";
}
.browse-widget:not(.no-item-numbers) .list>div:nth-of-type(6) .text:first-child:before {
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(6) .text:first-child:before {
content: "6";
}
.browse-widget:not(.no-item-numbers) .list>div:nth-of-type(7) .text:first-child:before {
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(7) .text:first-child:before {
content: "7";
}
.browse-widget:not(.no-item-numbers) .list>div:nth-of-type(8) .text:first-child:before {
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(8) .text:first-child:before {
content: "8";
}
.browse-widget:not(.no-item-numbers) .list>div:nth-of-type(9) .text:first-child:before {
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(9) .text:first-child:before {
content: "9";
}
.browse-widget:not(.no-item-numbers) .list .item:nth-of-type(10) .text:first-child:before {
content: "0";
}
.browse-widget .list>hr.separator {
opacity: 0.3;
@ -396,10 +399,10 @@
opacity: 0.5;
}
.browse-widget .list>div.heading:hover {
.browse-widget .list .item.heading:hover {
background: rgba(0, 0, 0, 0.6);
}
.browse-widget .list>div.heading.selected {
.browse-widget .list .item.heading.selected {
background: rgba(0, 0, 0, 0.7);
}
@ -410,15 +413,15 @@
/* Show item part on hover... */
.browse-widget .list>div .show-on-hover {
.browse-widget .list .item .show-on-hover {
opacity: 0;
}
.browse-widget .list>div:hover .show-on-hover {
.browse-widget .list .item:hover .show-on-hover {
opacity: inherit;
}
/* Show item part on select... */
.browse-widget .list>div .show-on-select {
.browse-widget .list .item .show-on-select {
opacity: 0;
}
.browse-widget .list>.selected .show-on-select {
@ -430,7 +433,7 @@
/****************************************************** Cloud List ***/
.browse-widget.cloud-view .list>div {
.browse-widget.cloud-view .list .item {
display: inline-block;
border-radius: 10px;
}
@ -439,7 +442,7 @@
display: block;
}
.browse-widget.cloud-view .list>div .text:first-child:before {
.browse-widget.cloud-view .list .item .text:first-child:before {
content: "";
}

View File

@ -1386,7 +1386,7 @@ module.Buttons = core.ImageGridFeatures.Feature({
var WidgetTestActions = actions.Actions({
testAction: ['- Test/',
testAction: ['Test/Test action',
function(){
console.log('>>>', [].slice.call(arguments))
return function(){
@ -1399,7 +1399,7 @@ var WidgetTestActions = actions.Actions({
// .testDrawer('Overlay', 'Header', 'paragraph')
// - show html in overlay with
// custom text...
testDrawer: ['Test/99: Drawer widget test...',
testDrawer: ['Test/99: D$rawer widget test...',
makeUIDialog('Drawer',
function(h, txt){
return $('<div>')
@ -1420,7 +1420,7 @@ var WidgetTestActions = actions.Actions({
focusable: true,
})],
// XXX show new features...
testBrowse: ['Test/-99: Demo new style dialog...',
testBrowse: ['Test/-99: Demo $new style dialog...',
makeUIDialog(function(){
var actions = this
@ -1429,7 +1429,7 @@ var WidgetTestActions = actions.Actions({
return browse.makeLister(null, function(path, make){
var that = this
make('select last')
make('select last')
.on('open', function(){
that.select(-1)
})
@ -1437,7 +1437,10 @@ var WidgetTestActions = actions.Actions({
make('do nothing')
.addClass('selected')
make('nested dialog...')
make('nested dialog...',
{
shortcut_key: 'n',
})
.on('open', function(){
actions.testBrowse()
})
@ -1445,7 +1448,7 @@ var WidgetTestActions = actions.Actions({
make('---')
make('close parent')
make('$close parent')
.on('open', function(){
that.parent.close()
})
@ -1467,7 +1470,7 @@ var WidgetTestActions = actions.Actions({
console.log('Dialog closing...')
})
})],
testBrowseCloud: ['Test/Demo cloud dialog...',
testBrowseCloud: ['Test/Demo $cloud dialog...',
makeUIDialog(function(){
var actions = this
@ -1510,7 +1513,7 @@ var WidgetTestActions = actions.Actions({
console.log('Dialog closing...')
})
})],
testList: ['Test/-99: Demo new style dialog...',
testList: ['Test/-99: Demo new style $lists in dialog...',
makeUIDialog(function(){
var actions = this
@ -1553,7 +1556,7 @@ var WidgetTestActions = actions.Actions({
})
})],
testProgress: ['Test/Demo progress bar...',
testProgress: ['Test/Demo $progress bar...',
function(text){
var done = 0
var max = 10

View File

@ -851,7 +851,8 @@ var KeyboardPrototype = {
if(drop
// explicit go to next section...
&& (!handler
|| handler.slice(0, 4) != 'NEXT')
|| (typeof(handler) == typeof('str')
&& handler.slice(0, 4) != 'NEXT'))
&& (bindings.drop == '*'
// XXX should this be more flexible by adding a
// specific key combo?

View File

@ -1049,6 +1049,22 @@ var BrowserPrototype = {
// 'last' - group traversable items at bottom
sortTraversable: null,
// Create item shortcuts...
//
// If false, no shortcuts will be created.
setItemShortcuts: true,
// Item shortcut text marker...
//
// This can be a regexp string pattern or a RegExp object. This
// should contain one group containing the key.
// Everything outside the last group will be cleaned out of the
// text...
//
// NOTE: it is best to keep this HTML compatible, this makes the
// use of chars like '&' not to be recommended...
itemShortcutMarker: '\\$(\\w)',
// Controls the display of the action button on each list item...
//
// Possible values:
@ -1204,7 +1220,7 @@ var BrowserPrototype = {
ctrl_Left: 'update!: "/"',
Backspace: 'Left',
Right: 'right',
P: 'push',
//P: 'push',
// XXX should these also select buttons???
Tab: 'down!',
@ -1221,16 +1237,16 @@ var BrowserPrototype = {
End: 'navigate!: "last"',
Enter: 'action',
O: 'action',
//O: 'action',
Esc: 'close: "reject"',
'/': 'startFilter!',
ctrl_A: 'startFullPathEdit!',
D: 'toggleDisabledDrawing',
H: 'toggleHiddenDrawing',
T: 'toggleNonTraversableDrawing',
ctrl_D: 'toggleDisabledDrawing',
ctrl_H: 'toggleHiddenDrawing',
ctrl_T: 'toggleNonTraversableDrawing',
// XXX should these use .select(..)???
// XXX should these be relative to visible area or absolute
@ -1245,6 +1261,14 @@ var BrowserPrototype = {
'#7': 'push!: "6!"',
'#8': 'push!: "7!"',
'#9': 'push!: "8!"',
'#0': 'push!: "9!"',
},
ItemShortcuts: {
doc: 'Item shortcuts',
pattern: '*',
},
},
@ -1267,7 +1291,6 @@ var BrowserPrototype = {
// .type - event type/name
// .args - arguments passed to trigger
//
//
// NOTE: event propagation for some events is disabled by binding
// to them handlers that stop propagation in .__init__(..).
// The list of non-propagated events in defined in
@ -1599,6 +1622,9 @@ var BrowserPrototype = {
//
// // element button spec...
// buttons: <bottons>,
//
// // shortcut key to open the item...
// shortcut_key: <key>,
// }
//
// <buttons> format (optional):
@ -1784,6 +1810,21 @@ var BrowserPrototype = {
var interactive = false
var size_freed = false
// clear previous shortcuts...
var item_shortcuts = this.options.setItemShortcuts ?
(this.keybindings.ItemShortcuts = this.keybindings.ItemShortcuts || {})
: null
// clear the shortcuts...
Object.keys(item_shortcuts).forEach(function(k){
if(k != 'doc' && k != 'pattern'){
delete item_shortcuts[k]
}
})
var item_shortcut_marker = this.options.itemShortcutMarker || /&(\w)/
item_shortcut_marker = item_shortcut_marker ?
RegExp(item_shortcut_marker)
: null
// XXX revise signature...
var make = function(p, traversable, disabled, buttons){
var opts = {}
@ -1825,7 +1866,7 @@ var BrowserPrototype = {
return res
}
// array of str/func...
// array of str/func/dom...
if(p.constructor === Array){
// resolve handlers...
p = p.map(function(e){
@ -1869,6 +1910,41 @@ var BrowserPrototype = {
.text(p.replace(dir, ''))
}
// keyboard shortcuts...
if(item_shortcuts){
// key set in options...
opts.shortcut_key && !item_shortcuts[opts.shortcut_key]
&& that.keyboard.handler(
'ItemShortcuts',
opts.shortcut_key,
function(){ that.push(res) })
// text marker...
if(item_shortcut_marker){
var _replace = function(){
// get the last group...
var key = [].slice.call(arguments).slice(-3)[0]
!item_shortcuts[key]
&& that.keyboard.handler(
'ItemShortcuts',
key,
function(){ that.push(res) })
return key
}
txt = txt.replace(item_shortcut_marker, _replace)
p.filter('.text')
.each(function(_, e){
e = $(e)
e.html(e.html().replace(item_shortcut_marker,
function(){
return '<span class="keyboard-shortcut">'
+_replace.apply(this, arguments)
+'</span>' })) })
}
}
interactive = true
// skip drawing of non-traversable or disabled elements if
@ -1889,6 +1965,7 @@ var BrowserPrototype = {
.append(p)
res.addClass([
'item',
// XXX use the same algorithm as .select(..)
selection && res.text() == selection ? 'selected' : '',
@ -1902,7 +1979,6 @@ var BrowserPrototype = {
opts.push_on_open && res.attr('push-on-open', 'on')
// buttons...
// button container...
var btn = res.find('.button-container')
@ -2140,7 +2216,7 @@ var BrowserPrototype = {
var that = this
var browser = this.dom
var elems = browser.find('.list>div:not(.not-searchable)'
var elems = browser.find('.list .item:not(.not-searchable)'
+ (this.options.elementSeparatorClass ?
':not('+ this.options.elementSeparatorClass +')'
: '')
@ -2498,7 +2574,7 @@ var BrowserPrototype = {
// NOTE: this uses .filter(..) for string and regexp matching...
select: function(elem, filtering){
var browser = this.dom
var pattern = '.list>div'
var pattern = '.list .item'
+ (this.options.elementSeparatorClass ?
':not('+ this.options.elementSeparatorClass +')'
: '')
@ -2619,7 +2695,7 @@ var BrowserPrototype = {
// .navigate('next') will simply navigate to the next element while
// .select('next') / .filter('next') will yield that element by name.
navigate: function(action, filtering){
var pattern = '.list>div'
var pattern = '.list .item'
+ (this.options.elementSeparatorClass ?
':not('+ this.options.elementSeparatorClass +')'
: '')

View File

@ -123,6 +123,8 @@ var WidgetPrototype = {
parent = this.parent = $(parent || 'body')
options = options || {}
this.keybindings = JSON.parse(JSON.stringify(this.keybindings))
// merge options...
var opts = Object.create(this.options)
Object.keys(options).forEach(function(n){ opts[n] = options[n] })
@ -201,9 +203,12 @@ var ContainerPrototype = {
// the client...
__init__: function(parent, client, options){
var that = this
parent = this.parent = $(parent || 'body')
options = options || {}
this.keybindings = JSON.parse(JSON.stringify(this.keybindings))
this.client = client
client.parent = this