http://bazaar.launchpad.net/~leo-editor-team/leo-editor/trunk/revision/1594
Now, you may want to add change you alt+a in leoSettings.leo:
-- @keys EKR bindings-->@shortcuts Other commands
act-on-node = Alt-A
This does the same thing as double-clicking on icon in Tk.
Note how simple creating your plugins in terms of act-on-node is. You
don't need boilerplate code to get a controller (c) in your plugin:
# defer binding event until c exists
def attachToCommander(t,k):
c = k.get('c')
event = c.config.getString('active_path_event') or "icondclick1"
leoPlugins.registerHandler(event, lambda t,k: onSelect(t,k))
def init():
leoPlugins.registerHandler('after-create-leo-frame', attachToCommander)
Many plugins can be as simple as registering their own act-on-node
handler like here:
g.act_on_node.add(active_path_act_on_node, priority = 90)
Note how it's in g, so there is no need for 'after-create-leo-frame.
There is no need for "configuration" and it's very easy to remember
(or better, there is basically no need for documentation apart from
telling the user what headline types the plugin adds act-on-node
support for).
--
Ville M. Vainio
http://tinyurl.com/vainio
I have ported active_path.py to use act_on_node. I pushed it to truk,
see the phenomenal amount of work at:
http://bazaar.launchpad.net/~leo-editor-team/leo-editor/trunk/revision/1594
Now, you may want to add change you alt+a in leoSettings.leo:
-- @keys EKR bindings-->@shortcuts Other commands
act-on-node = Alt-A
This does the same thing as double-clicking on icon in Tk.
> required, but that's beside the point). So how can active_path.py
> know that you're invoking act-on-node because you're
> wanting it to do something, as opposed to wanting some other enabled
> plugin to do something?
How do you determine this when you double-click the icon? ;-)
> I understand how plugins can evaluate the situation to decide if
> there's anything they can do, but I don't understand what happens when
> multiple plugins answer "yes" - I don't think priority is sufficient,
> which one I wish to act depends on my reason for requesting the action.
act-on-node only makes sense if a plugin can be certain that it should
handle this. If you can't be certain by node itself (headline, body),
you can take a look at uA's or parents. If your plugin provides a
feature that can be universally applied to any node, act-on-node
doesn't make sense.
Many plugins can be quite simple - active_path.py and @url are such
examples. Think of act_on_node as a keyboard version of file manager
double-click. It looks at the file and determines what to do with it.
You can right click on a file to get a richer set of options, but
double-click remains the obvious and fast-to-execute action.
> act-on-node only makes sense if a plugin can be certain that it should
> handle this. If you can't be certain by node itself (headline, body),
Additional note - if your plugin is written in such a way that it
can't be too confident about its nodes, it should pick a low priority.
Or add several handlers with different priorities. To use active-path
as an example, it could install a hight priority handler for @path
foo, but low priority handler for /foo/. Or, activating @path could
add uAs for all the children it creates, and act-on-node handler would
look at those uAs.
>
> On Mon, Feb 23, 2009 at 7:24 PM, Terry Brown
> <terry_...@yahoo.com> wrote:
>
> > required, but that's beside the point). So how can active_path.py
> > know that you're invoking act-on-node because you're
> > wanting it to do something, as opposed to wanting some other enabled
> > plugin to do something?
>
> How do you determine this when you double-click the icon? ;-)
That's a problem.
> > I understand how plugins can evaluate the situation to decide if
> > there's anything they can do, but I don't understand what happens
> > when multiple plugins answer "yes" - I don't think priority is
> > sufficient, which one I wish to act depends on my reason for
> > requesting the action.
>
> act-on-node only makes sense if a plugin can be certain that it should
> handle this. If you can't be certain by node itself (headline, body),
> you can take a look at uA's or parents. If your plugin provides a
> feature that can be universally applied to any node, act-on-node
> doesn't make sense.
Right, which is why I don't think it makes sense for active_path.py.
active_path.py will act on virtually any node which is a descendant of
a @path node. If you activate it on such a node where
p.h=="remember to call jerry" it will say
"create file @auto remember to call jerry", which is probably not what
you want.
You could argue that you shouldn't put a node "remember to call jerry"
in a tree which maps to the file system, but I don't agree, I think
it's the "hybrid view" that makes it useful. When I activate
active_path.py I'm saying "for now, treat this part of the tree as a
mapping to the filesystem and do something", I don't want to commit to
only ever treating this part of the tree as a mapping to the file
system.
I think the solution is probably to have plugins like active_path.py
provide a command and bind to the context menu, and make it easy (at
the leo level, not the plugin level) for the user to bind the various,
limited list of rclick, click, dclick etc. as they want for the plugins
they use. Allowing keyboard modifiers on click callbacks would be very
helpful.
Cheers -Terry
>
> On Mon, Feb 23, 2009 at 7:46 PM, Ville M. Vainio <viva...@gmail.com>
> wrote:
>
> > act-on-node only makes sense if a plugin can be certain that it
> > should handle this. If you can't be certain by node itself
> > (headline, body),
>
> Additional note - if your plugin is written in such a way that it
> can't be too confident about its nodes, it should pick a low priority.
> Or add several handlers with different priorities. To use active-path
> as an example, it could install a hight priority handler for @path
> foo, but low priority handler for /foo/. Or, activating @path could
> add uAs for all the children it creates, and act-on-node handler would
> look at those uAs.
Our emails crossed, but this hasn't changed my view. I don't think the
priority thing is sufficient, I see cases where two plugins are not
confident of their nodes. An active-path should act on a node
"cache.py", even if the node was just created by the user typing
"ctrl-Icache.py<RET>", so uAs don't work either.
Cheers -Terry
> Right, which is why I don't think it makes sense for active_path.py.
> active_path.py will act on virtually any node which is a descendant of
> a @path node. If you activate it on such a node where
> p.h=="remember to call jerry" it will say
> "create file @auto remember to call jerry", which is probably not what
> you want.
It can still apply in a limited form, where act-on-node reacts to:
- @path
- /foo/, when @path is one of the parents
- 'remember to call jerry', when os.path.isfile(...) and @path is one
of the parents. (questionable)
- Nodes created by active_path (uAs?).
> it's the "hybrid view" that makes it useful. When I activate
> active_path.py I'm saying "for now, treat this part of the tree as a
> mapping to the filesystem and do something", I don't want to commit to
> only ever treating this part of the tree as a mapping to the file
> system.
This is a good rationale to drop "remember to call jerry" from
triggers listed above.
> I think the solution is probably to have plugins like active_path.py
> provide a command and bind to the context menu, and make it easy (at
> the leo level, not the plugin level) for the user to bind the various,
> limited list of rclick, click, dclick etc. as they want for the plugins
> they use. Allowing keyboard modifiers on click callbacks would be very
> helpful.
Right.
I still think this makes sense for active_path, especially in limited
capacity. I also envision it will useful for "amateur" plugin makers
that only want to make simple plugins. As an example, think of being
able to prepend @done to p.h and pressing alt+a => node is moved to
"done" tree somewhere out of sight. You could make that with ~ 5 lines
of code.
> Our emails crossed, but this hasn't changed my view. I don't think the
> priority thing is sufficient, I see cases where two plugins are not
> confident of their nodes. An active-path should act on a node
> "cache.py", even if the node was just created by the user typing
> "ctrl-Icache.py<RET>", so uAs don't work either.
How do you differentiate this with current active_path.py?
> It can still apply in a limited form, where act-on-node reacts to:
More thoughts on this:
- The problem here is mostly a social one. The basic guideline is not
to write antisocial plugins that eat up act-on-node's from other, more
deserving plugins
- If you write an antisocial plugin, give it a low priority. If
someone's plugin proves problematic, the priority can be lowered
immediately by anyone with commit access. This is a very quick fix,
which solves the issue immediately without completely disabling the
plugin for everyone.
- Let's see a real case where it breaks things before worrying about
it. active_path.py is still the only place where it's used - we should
wait for a real life scenario where the plugins clash.
In its current form, act-on-node makes active_path.py immediately
usable without having to copy a @button node to your document (and
without mouse!). This also allows us to minimize the amount of
keybindings we need to have - think of act-on-url as a way plugins can
share a single keybinding with zero configuration.
> On Mon, Feb 23, 2009 at 8:19 PM, Ville M. Vainio <viva...@gmail.com> wrote:
>
>> It can still apply in a limited form, where act-on-node reacts to:
>
> More thoughts on this:
And even more (it seems I'm postponing washing dishes by brainstorming on this):
Perhaps leo should fire an "icon double-clicked" event on act-on-node
if no handler was found? That way, we would get the existing
double-click handlers "for free", while still avoiding
mouse-originated RSI.
Not sure which case you mean. For the "two plugins not confident of
their nodes" case, there's no differentiation, that's the issue.
For the "user created vs. read from filesystem 'cache.py'", I don't
think it's possible to differentiate, active_path.py should do the same
thing in both cases.
Cheers -Terry
>
> On Mon, Feb 23, 2009 at 8:19 PM, Ville M. Vainio <viva...@gmail.com>
> wrote:
>
> > It can still apply in a limited form, where act-on-node reacts to:
>
> More thoughts on this:
>
> - The problem here is mostly a social one. The basic guideline is not
> to write antisocial plugins that eat up act-on-node's from other, more
> deserving plugins
> - If you write an antisocial plugin, give it a low priority. If
> someone's plugin proves problematic, the priority can be lowered
> immediately by anyone with commit access. This is a very quick fix,
> which solves the issue immediately without completely disabling the
> plugin for everyone.
I still don't see priority as a way of handling this. I want to have 2
plugins loaded and be able to activate the one I want to activate based
on what's in my mind, without needing to guarantee that that's
derivable from the content of the tree.
> - Let's see a real case where it breaks things before worrying about
> it. active_path.py is still the only place where it's used - we should
> wait for a real life scenario where the plugins clash.
Here's one, only applies to me, but seems to illustrate how clashes
will happen immediately: I load a directory with active_path.py, and
there's a bunch of ESRI shapfiles in it. There are really a single
unit, but they're comprised of 3-7 files with the same basename and
different extensions. So I have a mini 'shapefile plugin' which, when
activated, groups all of these files under a single organizer node,
which I then annotate or whatever. So when I select one of these files
an use act-on-node, how can the mini 'shapefile plugin' and
active_path.py decide who I'm talking to - group the files or load one
of them? I don't want both to happen. Currently I activate the
shapefile plugin with a button.
One thought, Leo already has a repeat-complex-command, that or some
modified form of it might be useful here. I could leave the most
general action (active_path.py, for me at least) on double-click or
Alt-A or whatever, use a context menu the first time I want to group
the shapefiles, and then use the repeat-last-context-menu-cmd command
(from a key binding) to group other shapefiles.
Alternatively import from __future__ ESP.readUsersMind ;-)
Cheers -Terry
> I still don't see priority as a way of handling this. I want to have 2
> plugins loaded and be able to activate the one I want to activate based
> on what's in my mind, without needing to guarantee that that's
> derivable from the content of the tree.
Ok, that's a good example. Let's say you have a g.terrys_active_modes
= set(). In your act_on_node handler, you'll just check if you have
something enabled:
if not 'ESRI' in g.terrys_active_modes: raise leoPlugins.TryNext.
And your @buttons just add / remove modes to/from that set.
You can make that a high priority handler, since it's only enabled
when you want it to be enabled. There is no need to unload/reload
plugins to accomplish this (which necessarily demands less from the
plugins).
Ok, that's a good example. Let's say you have a g.terrys_active_modes
On Mon, Feb 23, 2009 at 9:02 PM, Terry Brown <terry_...@yahoo.com> wrote:
> I still don't see priority as a way of handling this. I want to have 2
> plugins loaded and be able to activate the one I want to activate based
> on what's in my mind, without needing to guarantee that that's
> derivable from the content of the tree.
= set(). In your act_on_node handler, you'll just check if you have
something enabled:
if not 'ESRI' in g.terrys_active_modes: raise leoPlugins.TryNext.
And your @buttons just add / remove modes to/from that set.
So my question is: do we want to muddy the design of plugins to handle special clicks? My feeling at present is: maybe not. Rather than trying to make sense of a design based on limited actions (clicks), it might be cleaner to create a design based on unlimited minibuffer commands.
> And as I reread this, I wonder whether a user options might be useful.
> Something like
>
> @string icon-double-click-command = act-on-node
> @string icon-ctrl-click-command = plugin-1-command-1
> @string icon-shift-click-command = plugin-2-command-1
>
> etc.
>
> *Maybe* this gives the user more flexibility than before. Maybe not.
This is sort of what I was trying to suggest, at one point. Rather
than, or in addition to, @settings, I think the user should be able to
make / change these bindings at runtime. Or provide a method to
reparse the relevant part of the current files @settings.
So I have a tree, I want to do X to a bunch of scattered nodes, so I
define a command to do X (somehow, @command nodes are only parsed at
start-up?) and then bind a click (or a key) and click (or key) all the
nodes to which I wish X to occur.
So one outcome would be that plugins should stop binding directly to
clicks, but should instead create commands, and those would need to be
advertised in some easy standard way (appear in the plugins menu under
'Commands'?
Cheers -Terry
So one outcome would be that plugins should stop binding directly to
clicks, but should instead create commands, and those would need to be
advertised in some easy standard way (appear in the plugins menu under
'Commands'?
I wonder how hard it would be to intercept the next menu selection, so
you could have a bind-next-menu-select to this key / click facility.
Tk menu items anyway bind directly to callbacks, so I don't see how
you'd intercept those. I imagine there's a signal from qt you could
use.
Cheers -Terry
>> So one outcome would be that plugins should stop binding directly to
>> clicks, but should instead create commands, and those would need to be
>> advertised in some easy standard way (appear in the plugins menu under
>> 'Commands'?
>
> Yes, this seems better than doing AI to determine what a click means.
But how would we determine what a click means? Make the user configure
it every time?
Mechanism like this is clearly needed for "take default action".
Keyboard has only so many keys, and commands may be hard to remember
(even with tab completion). Of course commands should be dead easy to
create (using just leoGlobals, which is always available) - but being
able to bind a default action is a big win. Obviously a default action
can directly invoke some command.
Think about installing an application that registers a file type
'.daz'. For shell, it can register actions in the right-click-menu,
but it can also optionally register itself as the default handler when
you double-click a .daz file in the ui shell.
What plugins should typically provide IMO:
- act-on-node handler (for item-doubleclick, alt+a)
- list of context menu entries (for right click - probably with every
item bound to a command + args).
Just providing commands is a usability mess, because users would have
to invent their own keyboard shortcuts from the pool of available
shortcuts (which is not too big, since leo doesn't currently support
emacs-like composite commands like C-x r s i a).
>> Yes, this seems better than doing AI to determine what a click means.
>>
>> And I like the new invention of a standard place to advertise
>> commands.
>
> I wonder how hard it would be to intercept the next menu selection, so
> you could have a bind-next-menu-select to this key / click facility.
> Tk menu items anyway bind directly to callbacks, so I don't see how
> you'd intercept those. I imagine there's a signal from qt you could
> use.
You can use this:
http://doc.trolltech.com/4.4/qmenu.html#triggered
However, I still think this sucks for the likes of @url nodes. You
typically only select one, and then move on to other activities.
> I still don't see priority as a way of handling this. I want to have 2
> plugins loaded and be able to activate the one I want to activate based
> on what's in my mind, without needing to guarantee that that's
> derivable from the content of the tree.
Perhaps you could use act-on-node to activate a plugin?
If your "context" has something like, say, @todo, you could active a
todo handling plugin by pressing alt-a on that. This could juggle what
next alt-a's do, or create some mode-specific @button's. Activating
that node again would disable the mode, getting rid of the buttons and
disabling alt-a actions. This way, your mode would not "clutter" the
ui when it's not active.
I admit I'm not a great fan of modefull ui's in general, but it's a thought.
>
> On Tue, Feb 24, 2009 at 7:05 PM, Edward K. Ream <edre...@gmail.com>
> wrote:
>
> >> So one outcome would be that plugins should stop binding directly
> >> to clicks, but should instead create commands, and those would
> >> need to be advertised in some easy standard way (appear in the
> >> plugins menu under 'Commands'?
> >
> > Yes, this seems better than doing AI to determine what a click
> > means.
>
> But how would we determine what a click means? Make the user configure
> it every time?
Yes... at least every time they want to change what the current
quick-fire action is, see below...
> Mechanism like this is clearly needed for "take default action".
> Keyboard has only so many keys, and commands may be hard to remember
> (even with tab completion). Of course commands should be dead easy to
> create (using just leoGlobals, which is always available) - but being
> able to bind a default action is a big win. Obviously a default action
> can directly invoke some command.
>
> Think about installing an application that registers a file type
> '.daz'. For shell, it can register actions in the right-click-menu,
> but it can also optionally register itself as the default handler when
> you double-click a .daz file in the ui shell.
>
> What plugins should typically provide IMO:
>
> - act-on-node handler (for item-doubleclick, alt+a)
> - list of context menu entries (for right click - probably with every
> item bound to a command + args).
I'm still not seeing a way of resolving conflicts without having to
provide so much information through tree annotation or other actions
that it defeats the point of a default action.
Think of a drawing program, there's a row of buttons down the side, you
activate one, and now, until I say different, 'click' means draw a
box. I'm done drawing boxes, so now I activate a different button, and
henceforth click means enable node editing.
For leo there's the possibility of key bindings as well as various
flavors of click, but I could see repeatedly rebinding
dbl-click, or Ctrl-Space, or Alt-A, or whatever, as the work I'm doing
on the tree changes, from move-these-nodes to the "ignore" subtree, to
collapse-these-nodes into a single holder, to load the file these nodes
point to, etc.
Perhaps the context based Alt-A binding you mentioned in another email
would be one way of doing this rebinding, which must be very quick and
easy, but that only works when there's only one obvious default action
provided by a plugin, and the plugin's context is easily identified, a
@todo or @bookmarks or something node might work, a @path node might
not.
So many plugins, like active_path.py for example, would create a
single command which is essentially their version of act-on-node, but
the user would control the key / click bindings to activate the
act-on-node type commands of different plugins.
There's another interface element Leo currently lacks, which would
probably be a big headache to introduce, multiple-node selection /
dragging / acting on. Ctrl-Click (or kbd equiv.) adds node to
selection sort of thing.
Cheers -Terry
> Perhaps the context based Alt-A binding you mentioned in another email
> would be one way of doing this rebinding, which must be very quick and
> easy, but that only works when there's only one obvious default action
> provided by a plugin, and the plugin's context is easily identified, a
> @todo or @bookmarks or something node might work, a @path node might
> not.
Yeah, it doesn't solve all of the gui interaction problems - but it's
the simplest possible way to implement (and debug!) a large scope of
plugin actions (including, I think, the modeful ones). Plugins that
override bindings are actually much more likely to override other
plugins, because there is no easy way to restore the old binding (as
opposed to just ignoring act-on-node's when mode is no longer
applicable, and letting the next handlers pick it up).
Response to multiselection on a new thread.
But how would we determine what a click means? Make the user configure
it every time?
Keyboard has only so many keys, and commands may be hard to remember
(even with tab completion).
Think about installing an application that registers a file type
'.daz'. For shell, it can register actions in the right-click-menu,
but it can also optionally register itself as the default handler when
you double-click a .daz file in the ui shell.
What plugins should typically provide IMO:
- act-on-node handler (for item-doubleclick, alt+a)
- list of context menu entries (for right click - probably with every
item bound to a command + args).
Just providing commands is a usability mess, because users would have
to invent their own keyboard shortcuts from the pool of available
shortcuts (which is not too big, since leo doesn't currently support
emacs-like composite commands like C-x r s i a).
>> But how would we determine what a click means? Make the user configure
>> it every time?
>
> I think clicks are a side show. But to answer your question, my proposal is
> for the user to be able to configure what a click means *once*, in a
> setting. It could be take-default-action or any other command.
User can't really configure context-sensitivity, it has to be done on
plugin side. I appreciate the wish to make this user-configurable: all
I really want is one global place (act-on-node) where plugins can
share a context-sensitive default action.
And I have that already :-). The only thing really missing is
replacing the default alt-A binding in default settings.
I very much dislike any kind of configuration - I want my stuff to
work "out of the box" in reasonable manner. If something is behind a
setting, apart from @enabled-plugins, the feature could as well not
exist for me. I have spent enough time in my life tweaking my .emacs
file that I want to avoid that as much as possible. Especially since
stuff can be *made* to work out of the box, and configuration is often
a cop-out.
Applicable quote from http://ometer.com/free-software-ui.html
QQQ
Preferences keep people from fixing real bugs. One of the more amusing
functions in GNU Emacs is "menu-bar-enable-clipboard." Now that KDE is
fixed, Emacs is basically the last remaining X application that
insists on having cut and paste that doesn't work correctly. So they
have this function "menu-bar-enable-clipboard" which basically means
"please make my cut and paste work correctly." Why is this an option?
I call this kind of preference the "unbreak my application please"
button. Just fix the app and be done with it.
QQQ
> Naturally, we then have to worry about what take-default-action means :-)
> But that's a different problem! Do you see? We have decoupled the
> association of clicks (and variant clicks) with commands. We can no deal
> with both questions separately, which removes unnecessary interactions.
Yeah, but I consider this a solved problem, through act-on-node (for
plugins where it makes sense). Imagine if you had to bind a specific
key (say, alt+u) to "open @url node if this makes sense for this
node". That would be clean and logical, but with horrible usability.
"You are the computer, you figure it out", I'd say.
So basically, I don't even see what the whole problem currently is. I
probably miscommunicated by saying act-on-node solves all of the
problems for all of the plugins for everybody. Chalk that up as
exaggeration. What it is, is a new feature that provides a powerful
tool for plugin writers that provides good, easy-to-code usability
without hooking explicitly to ui events. I see it as a "general"
solution, but you can only seek certain level of generality without
succumbing to analysis paralysis and over-engineering.