revised focus handling + long path scrolling...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2015-06-26 14:40:41 +03:00
parent 4501fb7835
commit ea3c497b69
2 changed files with 79 additions and 14 deletions

View File

@ -40,7 +40,19 @@
white-space: nowrap; white-space: nowrap;
padding-right: 30px; padding-right: 30px;
opacity: 0.8;
/* XXX need a way to make this automatic and depend on .browser
setup to avoid multiple scrollbars and the need to manually
dive into the configuration to change the container size
limits
*/
max-width: 50vh;
overflow: auto;
}
/* XXX not sure about this... */
.browse .path::-webkit-scrollbar {
width: 5px;
height: 5px;
} }
.browse .path:hover { .browse .path:hover {
opacity: 1; opacity: 1;
@ -48,12 +60,14 @@
.browse .path:empty { .browse .path:empty {
display: block; display: block;
} }
.browse:not(.flat) .path:before { .browse:not(.flat) .path:not([contenteditable]):before {
content: "/"; content: "/";
} }
.browse .path .dir { .browse .path .dir {
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
opacity: 0.8;
} }
.browse .path .dir:after { .browse .path .dir:after {
content: "/"; content: "/";
@ -118,6 +132,15 @@
cursor: pointer; cursor: pointer;
opacity: 0.7; opacity: 0.7;
/* XXX need a way to make this automatic and depend on .browser
setup to avoid multiple scrollbars and the need to manually
dive into the configuration to change the container size
limits
*/
max-width: 50vh;
overflow: hidden;
} }
.browse:not(.flat) .list div:not(.not-traversable):after { .browse:not(.flat) .list div:not(.not-traversable):after {
content: "/"; content: "/";

View File

@ -100,6 +100,7 @@ var BrowserPrototype = {
filter: true, filter: true,
// Enable/disable full path editing... // Enable/disable full path editing...
// NOTE: as with .filter above, this only affects .startFullPathEdit(..)
fullpathedit: true, fullpathedit: true,
// If false will disable traversal... // If false will disable traversal...
@ -145,7 +146,6 @@ var BrowserPrototype = {
Esc: 'abortFullPathEdit!', Esc: 'abortFullPathEdit!',
}, },
// filter mappings...
Filter: { Filter: {
pattern: '.browse .path div.cur[contenteditable]', pattern: '.browse .path div.cur[contenteditable]',
@ -336,6 +336,8 @@ var BrowserPrototype = {
// value to the html attr will not affect the actual path. // value to the html attr will not affect the actual path.
// NOTE: .path = <some-path> is equivalent to .update(<some-path>) // NOTE: .path = <some-path> is equivalent to .update(<some-path>)
// both exist at the same time to enable chaining... // both exist at the same time to enable chaining...
// NOTE: this will scroll the path to show the last element for paths
// that do not fit in view...
// //
// XXX need a way to handle path errors in the extension API... // XXX need a way to handle path errors in the extension API...
// ...for example, if .list(..) can't list or lists a different // ...for example, if .list(..) can't list or lists a different
@ -346,6 +348,7 @@ var BrowserPrototype = {
path = path || this.path path = path || this.path
var browser = this.dom var browser = this.dom
var that = this var that = this
var focus = browser.find(':focus').length > 0
// normalize path... // normalize path...
path = this.path2lst(path) path = this.path2lst(path)
@ -410,14 +413,27 @@ var BrowserPrototype = {
} }
})) }))
// handle path scroll..
var e = p.children().last()
// scroll to the end when wider than view...
if(e.length > 0 && p.width() < p[0].scrollWidth){
// scroll all the way to the right...
p.scrollLeft(p[0].scrollWidth)
// keep left aligned...
} else {
p.scrollLeft(0)
}
// fill the children list... // fill the children list...
var interactive = false var interactive = false
var make = function(p){ var make = function(p){
interactive = true interactive = true
return $('<div>') return $('<div>')
.click(function(){
// handle clicks ONLY when not disabled... // handle clicks ONLY when not disabled...
.click(function(){
if(!$(this).hasClass('disabled')){ if(!$(this).hasClass('disabled')){
that.push($(this).text()) that.push($(this).text())
} }
@ -435,6 +451,11 @@ var BrowserPrototype = {
this.dom.attr('path', '/' + this.path.join('/')) this.dom.attr('path', '/' + this.path.join('/'))
this.trigger('update') this.trigger('update')
// maintain focus within the widget...
if(focus && browser.find(':focus').length == 0){
this.focus()
}
return this return this
}, },
@ -587,15 +608,19 @@ var BrowserPrototype = {
// internal actions... // internal actions...
// XXX full path editing... // full path editing...
//
// NOTE: the event handlers for this are set in .__init__()...
//
// XXX should these be a toggle??? // XXX should these be a toggle???
startFullPathEdit: function(){ startFullPathEdit: function(){
if(this.options.fullpathedit){ if(this.options.fullpathedit){
var browser = this.dom var browser = this.dom
var path = this.path.join('/') var path = '/' + this.path.join('/')
var orig = this.select('!').text()
browser browser
.attr('orig-path', path) .attr('orig-path', path)
.attr('orig-selection', this.select('!').text()) .attr('orig-selection', orig)
var range = document.createRange() var range = document.createRange()
var selection = window.getSelection() var selection = window.getSelection()
@ -634,12 +659,17 @@ var BrowserPrototype = {
var e = browser.find('.path') var e = browser.find('.path')
.removeAttr('contenteditable') .removeAttr('contenteditable')
this.path = path || '/' + e.text() this.path = path || e.text()
return this.focus() return this
.focus()
}, },
// path filtering...
//
// NOTE: this uses .filter(..) for actual filtering... // NOTE: this uses .filter(..) for actual filtering...
//
// XXX add support for '/' in filter...
// XXX revise API -- seems a bit overcomplicated... // XXX revise API -- seems a bit overcomplicated...
showFiltered: function(pattern){ showFiltered: function(pattern){
var that = this var that = this
@ -708,8 +738,6 @@ var BrowserPrototype = {
this.dom.find('.path .dir.cur') this.dom.find('.path .dir.cur')
.text('') .text('')
.removeAttr('contenteditable') .removeAttr('contenteditable')
this
.focus()
// NOTE: we might select an item outside of the current visible // NOTE: we might select an item outside of the current visible
// area, thus re-selecting it after we remove the filter // area, thus re-selecting it after we remove the filter
@ -717,6 +745,7 @@ var BrowserPrototype = {
this.select(this.select('!')) this.select(this.select('!'))
return this return this
.focus()
}, },
get filtering(){ get filtering(){
return this.dom.find('.path .dir.cur[contenteditable]').length > 0 return this.dom.find('.path .dir.cur[contenteditable]').length > 0
@ -990,6 +1019,8 @@ var BrowserPrototype = {
action: function(){ action: function(){
var elem = this.select('!') var elem = this.select('!')
//this.focus()
// nothing selected, select first and exit... // nothing selected, select first and exit...
if(elem.length == 0){ if(elem.length == 0){
this.select() this.select()
@ -1102,6 +1133,7 @@ var BrowserPrototype = {
// an element is not traversable/listable and will trigger the // an element is not traversable/listable and will trigger the
// .open(..) on push... // .open(..) on push...
// //
// XXX need a way to constructively communicate errors up...
list: function(path, make){ list: function(path, make){
path = path || this.path path = path || this.path
var m = this.options.list var m = this.options.list
@ -1124,9 +1156,19 @@ var BrowserPrototype = {
// basic permanent interactions... // basic permanent interactions...
dom.find('.path') dom.find('.path')
// NOTE: these are used for full-path editing and are defined
// here in contrast to other feature handlers as the
// '.path' element is long-lived and not rewritten
// on .update(..)
.dblclick(function(){ .dblclick(function(){
that.startFullPathEdit() that.startFullPathEdit()
}) })
.keyup(function(){
var e = $(this)
if(e.attr('contenteditable') && e.text() != dom.attr('orig-path')){
dom.find('.list').empty()
}
})
// add keyboard handler... // add keyboard handler...
dom.keydown( dom.keydown(