The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Text::Editor::Easy::Events - Manage events linked to user code : specific code is referenced and called here.

VERSION

Version 0.49

INTRODUCTION

'Editor' instances will stand for 'Text::Editor::Easy' instances.

'Editor' instances have already a default management for a few events : mouse clic (set new cursor position), key press (insert or delete text), mouse drag (select text), resize.... What you may want to do when you define your special code in response to events must be explained :

  • Link code to an event not managed by default (for instance, the mouse motion)

  • Add an action to an already managed event

  • Inhibit the default action and make your own instead

  • Just inhibit the default action (here you don't write code)

  • Have your specific code executed in an asynchronous way (by a specific thread) in order to make a non freezing huge task

  • Have you specific code executed by a specific thread in a synchronous way

  • Have all these possibilities defined during the 'editor' instance creation or later.

  • Link more code to an already linked event (more than one specific sub for only one event) ...

As you see, event management is a nightmare. What could be the interface that would enable all this and would still be usable ?

As usual, easy things should be done lazily but difficult tasks should always be possible with, of course, a little more options to learn.

EASY THINGS

 my $editor = Text::Editor::Easy->new( 
    {                                     # start of editor new options
        'file'   => 'my_file.t3d',        # option 'file' has nothing to do with event management
        'events' => {                     # events declaration
            'clic' => {                   # first specific management, 'clic' event
                'sub' => 'my_clic_sub',
            },                            # end of clic event
            'motion' => {                 # second specific management, 'motion' event
                'sub' => 'my_motion_sub',
                'use' => 'My_module',     # as in perl 'use My_module' : without .pm extension
            },                            # end of motion event
        }                                 # end of events declaration
    }                                     # end of editor new options
 );                                       # end of new
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $clic_info_ref ) = @_;
 
     [...]
 }

'events' option

You can link your subs to events during the 'editor' instance creation with the 'events' option. This option takes a hash as a value. The keys of this hash are the name of the events : in the example, 'clic' and 'motion' events are managed. So, the first thing you have to know is the name of the events :

  • clic, happens when you press the left button of your mouse in the editor

  • motion, happens when you move your mouse above the editor

  • drag, happens when you move your mouse above the editor with the left button of your mouse pressed

  • change, happens when the text of the editor is changed (text added, deleted or replaced).

  • cursor_set, happens when the cursor position is changed whatever the cause (insert, clic, direction keys, program,...)

  • wheel, happens when the user rolls the mouse wheel

  • right_clic and double_clic, other mouse events

  • key events have to be named more precisely (for instance, 'alt_b_key' or 'ctrl_e_key')

'sub', 'use' and 'package' options of one particular event in 'events' option

For each event managed, you have another hash which will contain, at least, the 'sub' option. Yes, this makes quite a lot of hashes, but they are the best way to make easy interfaces : you don't have to learn arbitrary positions (just think of other major langages), if the key names are well chosen, you learn the interface just reading an example and your code is auto-documented. I wonder how can other langages still exist without hashes...

Now, if you give nothing more than the 'sub' option, your sub should be visible and in the 'main' package. This point could be explained further. For simple things, you write your 'sub' in the same perl program file that makes the 'use Text::Editor::Easy;' call and you don't use perl package instruction.

If your program is more complex with more than one file, you can add the 'use' option which should indicate the name of a module that contains your sub. Be careful ! The default package is now assumed to have the same value as the module. If this is not true, you'll have to add 'package' option too :

 'motion' => {                     # 'motion' event of 'events' option
     'sub'     => 'my_motion_sub',
     'use'     => 'My_module',
     'package' => 'My_package',    # sub 'my_motion_sub' of 'My_module' is
                                   #     after a 'package My_package;' declaration
 },                                # end of 'motion' event

If you have used the perl 'package' instruction in your main program, you may use only the 'package' option without the 'use' option (in order not to have the 'main' default package assumed).

What about your specific 'sub' ?

Here are the 2 remaining things that have to be known :

  • What will your sub receive ?

  • What should return your sub ?

received information

 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;             # $info_ref is a hash reference
 
     $editor->insert(
         ' useless text ',
         {
             'line' => $info_ref->{'line'},      # The insertion point will be
             'pos'  => $info_ref->{'pos'},       # the mouse clic position
         }
     );                                          # End of insert call
 }

You always receive 2 parameters :

  • The 'editor' instance that has received the event.

  • A hash reference that contains information relative to the event.

Of course, you can't expect the information to be the same for a key press and for a mouse motion. The number and names of the hash keys will then depend on the event itself. All keys are explained for each event here. But it's easier to see all the possibilities for the keys and guess what you'll get for your event :

  • 'line' : a 'line' instance.

  • 'pos' : a position in that line

  • 'x' : an absisse (for 'hard' events)

  • 'y' : an ordinate (for 'hard' events)

return value, 'action' option introduction

For easy things, your return value is not used. After your specific sub has been executed, the default management will be done (if any) with the same event information ($info_ref hash) that you have received.

But if you want your sub to be the last thing to be done in response to the event, you can add the 'action' option with the 'exit' value :

 my $editor = Text::Editor::Easy->new(
     {
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'action' => 'exit',            # nothing will be done after 'my_clic_sub'
             },
         }
     }
 );

In this case, your sub will always be the last executed action. Sometimes, you would like to decide, according to the event information (so dynamically), if you want to go on or not. See here for dynamic exit.

In a more vicious way, you may want to change the values of the event information in order to change the data on which the default management will work. Again, the 'action' option gives you the power to lie.

A good easy thing would be that if 'action' option (with 'exit value) is present without the 'sub' option, then nothing is executed, just an exit is made :

 'events' => {
     'clic' => {
         'action' => 'exit',         # nothing will be done, no 'sub' option
     },
 }                                   # end of events declaration

As you see, 'sub' option is, in fact, not mandatory.

easy things conclusion

Nothing has been said about threads and dynamic event linking, but this is quite normal for the easy part of the interface. Still you can do half of what has been introduced at the beginning.

In an easy way, all events are done synchronously by the 'Graphic' thread : the 'Graphic' thread will have to complete your sub. So you may feel desperate if you have a huge task to do in response to a user event and if you still want your application to remain responsive : which seems incompatible, but...

EVENT INTERFACE SUM UP

Instance creation

There are 2 options in the 'new' method that deals with events :

Instance update

In order to update event management, here are the methods :

Information on events

In order to inquire the event management, here are the methods :

EVENT NAMES AND SEQUENCE

sequence of a 'true event', 'labels'

For each 'true event', a sequence of actions is done. Each action can be either an event action (= user action = private label) or a default action, named with a 'label'. For instance, the 'clic' sequence of the 'clic' 'true event' contains the following actions :

  • _calc_line_pos, default action, calculates 'line' and 'pos' from 'x' and 'y' coordinates of the 'clic'.

  • any_any_clic, user action (generated event)

  • any_clic, user action (generated event)

  • clic, user action (generated event)

  • _test_resize, default action, starts a drag sequence in order to resize the editor zone (only done if the cursor shape looks like a double arrow, either vertical or horizontal).

  • _set_cursor, default action, unselects previously selected text, positions cursor, sets the focus.

  • any_after_clic, user action (generated event)

  • after_clic, user action (generated event)

So there is a big difference between a 'true event' (the 'clic' made by the user) and an 'event action' (code linked to an event but at a precise moment in the sequence). In the previous 'clic' example, you have to choose between 5 event names to place your user action in the sequence.

Each user action is linked using the label of the generated event. For instance, if you want to use the 'after_clic' event instead of 'clic' event, you just have to write :

 my $editor = Text::Editor::Easy->new( 
    {
        'events' => {
            'after_clic' => {             # 'after_clic' event, done after default
                'sub' => 'my_sub',        # management (generated 'clic' event is done before)
            },
        }
    }
 );

Still, the 'after_clic' is not a 'true event', it's just an action in the sequence. The difference is important because you can define your own sequence but you'll have to use the 'true event' name to do that.

In the sequence, you can easily guess which is an event and which is a default action thanks to the beginning : everything that begins with '_' is a default action otherwise, it's a generated event.

A generated event is just a predefined private label : the label is already defined in the default sequence but you have to link your action to that label (no action by default).

When you create your own sequence, everything that does not start with a '_' is considered as a private label and private code linked to that label is searched.

So event names used with the 'events' option are just private labels contained in a 'true event' sequence.

In conclusion, by default, the 'true event' 'clic' is linked to a sequence that contains 5 possible private labels (named 'clic', 'after_clic', ...).

modifier keys

The modifier keys, which are 'alt', 'ctrl' or 'shift', bring complexity in a too simple event management. In the following explanations 'modifier keys' will be called 'meta keys'...because it's shorter.

As you may know, these keys or any combination of these keys can be associated with a standard key press, mouse clic, mouse motion, ... and you could think of that in 2 ways :

  • the event mixed with a meta-key is the same, there is just more information

  • the event mixed with a meta-key is another one and should be named differently

As a programmer, the first approach can lead to single sub managing different events (which is not very clear), but the second one can lead to multi-declaration pointing to the same sub, which is too verbose (and not very clear in the end).

So let's have the 2 possible ways : it's your business to choose the one that is the more efficient according to your wish. For any combination of 'meta-keys', you'll have to add any_ prefix. For a particular combination, you'll have to add the 'combination string' as prefix, for instance alt_ or ctrl_shift_. Note that in the 'combination string', 'meta-keys' are listed in alphabetic order : 'alt', 'ctrl' and then 'shift'.

As an example, you could press 'alt' when making a 'clic' with your mouse. The 'true event' name will be 'alt_clic' and the sequence will be the following :

  • _calc_line_pos, default action, calculates 'line' and 'pos'

  • any_any_clic, user event

  • any_clic, user event, 'clic' with any combination of 'meta_keys'

  • alt_clic, user event, only done when 'alt' key is pressed during the 'clic'

  • any_after_clic, user event, 'after_clic' with any combination of 'meta_keys'

In conclusion, the 'true event' 'alt_clic' is linked to a sequence that contains 4 private labels (named 'any_clic', 'alt_clic', ...).

The 'any_clic' is then done for a simple 'clic' and for any combination of meta-keys (including 'alt').

 my $editor = Text::Editor::Easy->new( 
    {
        'events' => {
            'alt_clic' => {               # 'alt_clic' event, 'alt' key pressed
                'sub' => 'my_alt_sub',
            },
            'any_clic' => {               # 'any_clic' event, whatever meta-keys
                'sub' => 'my_any_sub',
            },
        }
    }
 );

about 'any_any_clic'

'clic' is in fact the left simple clic. Right simple clic and left double clic are often used too. For instance, the true event 'shift right simple clic', named 'shift_right_clic', has the following sequence :

  • _calc_line_pos, default action, calculates 'line' and 'pos'

  • any_any_clic, user event, any type of 'clic', with any meta-keys

  • any_right_clic, user event, 'right_clic' with any combination of 'meta_keys'

  • shift_right_clic, user event, only done when shift meta-key is pressed

any_any_clic is then done whatever the 'clic' (simple, double or right) and whatever the meta-keys.

changing the sequence

'sequence' option in a 'true event'

During instance creation, if you want a different sequence from the default one, you can define your own with the 'sequence' option in the 'true event' declaration.

In the sequence, each label that does not start with '_' is considered as an event (= private label).

So you can create your own events in your sequence. But this is not a real event creation, because your event depends on a 'true event' to be generated. Creating a 'true event' will be another matter.

 my $editor = Text::Editor::Easy->new( 
    {
        'events' => {
            'alt_clic' => {
                'sequence' => [            # changing 'alt_clic' sequence
                    '_calc_line_pos',      # getting 'line' and 'pos'
                    'my_foo_clic',         # private label (declared in this sequence)
                    '_set_cursor',         # not done by default 'alt_clic'
                ],
            },
            'my_foo_clic' => {             # 'my_foo_clic' label definition, whatever the sequence
                'sub'     => 'foo_sub',    # 
                'thread'  => 'Foo',        # complete interface possible as any predefined event
            },
        }
    }
 );

Defining such a sequence is somewhat 'static'. For each 'alt_clic' event of this instance, this sequence will be used. You may want to have very specific sequences from time to time, and use the 'default' one or your 'static' one the rest of the time. These dynamic sequences are possible thanks to the 'action' option.

'sequences' key of the 'new' method

The previous example could have been written like this as there is just a sequence defined for the 'alt_clic' event (no action is really done) :

 my $editor = Text::Editor::Easy->new( 
    {
        'sequences' => {
            'alt_clic' => [                # changing 'alt_clic' sequence
                '_calc_line_pos',
                'my_foo_clic',
                '_set_cursor',
            ],
        },                                 # End of 'sequences' declaration
        'events' => {
            'my_foo_clic' => {             # 'my_foo_clic' label definition
                'sub'     => 'foo_sub',
                'thread'  => 'Foo',
            },
        }
    }
 );

'sequences' key is at the same level as 'events' key. You can change sequences without writing event code.

With this syntax, you can change as many sequences as you wish : the keys of the 'sequences' hash are the true event names, and the values are array references representing the sequences.

Note that if a sequence is defined both in a 'sequence' of a 'true event' and as a sub-key of the 'sequences' key, the 'sequences' definition is taken.

'ACTION' OPTION

For each event, if 'action' option is used, its value must be one of the following.

  • 'exit' to exit from the sequence.

  • 'change' to change event values

  • 'jump' to go straight to a precise label

  • 'reentrant' to create a dynamic sequence

  • 'nop' to stop an interruptible thread

With 'change', 'jump' and 'reentrant' values, the return value of your specific sub is used.

'exit' value

As seen in the easy part of the interface, you can put an 'exit' action alone or with a 'sub' option. In the second case, your sub will be the last action executed in the event sequence.

'change' value

 my $editor = Text::Editor::Easy->new( 
     {
         'file'   => 'my_file.t3d',
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'action' => 'change',     # event information can be changed by 'my_clic_sub'
             },
         }
     }
 );                                        # end of new
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;
 
     $info_ref->{'pos'} = 0;  # setting position to the beginning of the line
     return $info_ref;        # Returning a hash reference with the same keys,
                              #     'pos' value probably changed (was perhaps already 0)
 }

With 'change' value you can modify if you wish the values of the hash reference $info_ref which contains your event information. The following labels of the sequence (including yours) will use your new values (if there are no more change action).

Any hash reference returned will be considered as the new "info", any other return value will be ignored : no change will be assumed. If you want to exit dynamically, you have to use 'jump' value.

'jump' value

 my $editor = Text::Editor::Easy->new( 
     {
         'file'   => 'my_file.t3d',
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'action' => 'jump',                     # a jump can be done
             },
         }
     }
 );
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;
 
     my $line = $info_ref->{'line'};
     if ( $line->text ) ) {
         $info_ref->{'pos'} = 0;
         return $info_ref;                               # no jump, values changed
     }
     while ( ! $line->text ) {
        $line = $line->next;
        if ( ! defined $line ) {
            $line = $editor->last;
            last;
        }
     }
     my %new_info = ( 
         'line' => $line,
         'pos'  => 0 ),
     );
                                                         # jump to '_set_cursor' label
     return [ '_set_cursor', \%new_info ];               # providing the hash required
                                                         # here, only '_test_resize' has been jumped
 }

You may return from your specific sub managing a 'jump' action in 3 different ways :

  • returning undef or any scalar value will be ignored (no 'jump' action assumed).

  • returning a hash reference will just make a 'change' action with no jump. The hash given will be taken as the new <$info_ref> hash (for default management...) : a 'jump' action can encapsulate a 'change' action

  • returning an array reference will make a jump : the first position of the array is the label where to jump, the second one (optional) is the new $info_ref hash. With no second element, info is unchanged. You can specify 'exit' or '_exit' for the label where to jump : in that case, you've made a dynamic exit.

smallest possible 'jump'

As you may link more than one sub to one event, the smallest possible jump is done when you give the label following your own one. In this case, what you have jumped are the possible other subs that were linked to the same label as yours, these subs should have been done after your own one (if no jump has been made).

about info keys and labels

If the labels that you've jumped would have added keys to the information hash and if these keys are needed for the labels you jump to, you may have to provide these keys yourself.

'reentrant' value

 my $editor = Text::Editor::Easy->new( 
     {
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'action' => 'reentrant',     # dynamic sequence enabled
             },
             # Defining private labels
             'search_error'      => { 'sub' => 'search_error' },
             'highlight_error'   => { 'sub' => 'h_err', 'action' => 'reentrant' },
             'error_information' => { 'sub' => 's_info', 'thread' => 'Info' },
         }
     }
 );
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;

     if ( $info_ref->{'line'}->text =~ /error/i ) {
         return [ [ 'seach_error', 'highlight_error', 'error_information', '_exit' ] ];
     }
 }

'reentrant' value for action option includes all the 'jump' possibilities plus the dynamic sequence.

A dynamic sequence is just a sequence of labels (default or private ones). Note that this sequence is done after your sub, then the normal sequence is going on (that's why the 'reentrant' name has been given). You can make endless loops if a reentrant label calls itself.

If you want the 'dynamic sequence' functionality without the 'reentrant' one, you can add an '_exit' label at the end of you dynamic sequence (last action to be done).

Action made according to 'reentrant' return value sub :

  •  return;
     return 'foo';

    With any scalar (maybe undef), nothing in particular is done (no event change, no jump...).

  •  return { 'x' => 125, 'y' => 12 };

    A hash reference is interpreted as the new information hash. Here, a change action is understood.

  •  return [ 'foo_label' ];
     return [ 'foo_label', { 'x' => 111, 'y' => 45 } ];
     
     return [ [ 'foo_label', 'bar_label' ] ];
     return [ [ 'foo_label', '_jump' ], { 'x' => 111, 'jump' => 'bar_lalel' } ];

    With an array reference value, the second optional parameter is the new information hash if a change action is needed (when not present, no information change is assumed).

    For the first parameter, there are 2 possibilities :

    • If it's a scalar, it's supposed to contain the next label to be executed (here, a jump action is assumed).

    • If it's an array reference, a 'reentrant' action is assumed : each element is a label to be executed, the normal sequence will be continued after these dynamic labels.

'nop' value

This special value should always be used with the 'thread' option (read further, THREADS CONTRIBUTION).

If a sub executed by a thread is huge, you may write it in an interruptible way (writing instructions like return if anything_for_me; from time to time). A 'nop' action just sends a false task to your thread (there is something for it and it's ... nothing !) in order to interrupt its long task.

THREADS CONTRIBUTION

Just imagine the future : computers with more than one CPU... No sorry, that's just present : as for me, I have a dual core. But imagine that, as a programmer, you could use your 2 (or maybe more) CPU very easily : for instance, just using threads...

As you can add threads to manage new methods with 'Text::Editor::Easy' objects, you can use (or create) as many threads as you want to manage events. Of course, dividing a precisely defined job into more pieces than you have CPU won't be more efficient. But very often, with interactive applications, we don't have a precise job to do : tasks to be done change sometimes so fast, depending on user actions, that what was interesting to do at one moment could be useless just a few milliseconds later. With a multi-thread application, you not only give yourself the power to use all of your CPU, but you also give yourself the power to interrupt useless tasks.

When you use the 'Graphic' default thread, your event sub is synchronous, that is the code of your event will freeze the user interface : you should not use it for heavy tasks. For little tasks, this freeze won't be noticed. If you have a huge task to do in response to an event, you can use another thread than the 'Graphic' one. In this case, the 'Graphic' thread still receive the initial event (you can't change that !) but as soon as enough information has been collected, your thread is called asynchronously by the 'Graphic' thread (the 'Graphic' thread won't wait for your thread response). And here, if you make a heavy task, the user interface won't be freezed.

Still, with any thread, you should work in an interruptible way rather than make a huge task at once. Why ? Because the principle of events is that you can't know when they occur and how many. Suppose your code responds to the mouse motion event : when the user moves his mouse from left to right of your editor, you can have more than 10 mouse motion events generated in one second. And a perfect response to the first event can be useless as soon as the second event has occured. Moreover, the 'Graphic' thread will send all asynchronous events to your thread, even if it is busy working. Events will stack in a queue if your thread can't manage them quickly. If your code makes, in the end, a graphical action visible by the user, there could be a long delay between the last event and its visible action. And the user would consider your code as very slow. On the contrary, if you work in an interruptible way, that is, if you insert, from time to time, little code like that :

 return if (anything_for_me);       # "me" stands for your thread executing your code

the user could have the feeling that your code is very fast : this is because you empty your thread queue more quickly and thus decrease the delay between the last event and its answer. But you should add this line (that is, check your thread queue) when you are in a proper state : just imagine that there is really something for you and that your code will be stopped and executed another time from the start.

The conclusion is : "A good way to be fast is to give up useless tasks" and using more than one thread allows you to give up, so don't hesitate to give up. This is the very power of multi-threaded applications : the ability to make huge tasks while remaining responsive. This does not mean that programmers can still be worse than they are now (me included !) : they have to know where to interrupt.

'thread' option

 my $tid = Text::Editor::Easy->create_new_server(
     {
         ... # see Text::Editor::Easy::Comm for mandatory options
         'name' => 'My_thread_name',
     }
 );
 my $editor = Text::Editor::Easy->new( 
     {
         'file'   => 'my_file.t3d',
         'events' => {
             'clic' => {
                 'sub'    => 'my_clic_sub',
                 'thread' => 'My_thread_name',    # $tid could have been used
                                                  # instead of 'My_thread_name'
             },
         }
     }
 );

The value of the 'thread' option is the name of the thread you have chosen (should contain at least one letter), or the 'tid' (thread identification in perl ithread mecanism) that the program has chosen for you (it's an integer).

Note that by default, if you give the 'thread' option, an asynchronous call is assumed. The 'Graphic' thread asks your thread to execute your sub but doesn't wait for its response.

'create' option

In the 'thread' option example, the thread had already been created, but if you use 'thread' option with a name that is unknown, a new thread will be created on the fly and will be named accordingly.

On the contrary, if you have written a bad name by mistake, you may want to prevent this auto-creation. The 'create' options has 3 possible values :

  • 'warning' : if the thread does not exist yet, the thread is created but a display is made on STDERR

  • 'unlink' : if the thread does not exist yet, the thread is not created, the event is not linked to your sub but the 'editor' instance is still created

  • 'error' (or any value different from 'warning' and 'unlink') : if the thread does not exist, the thread is not created, the 'editor' instance is not created

Maybe you feel that the 'create' option should have been used to enable creation not to prevent it. But you are a perl programmer and should feel responsible : the more irresponsible the languages assume the programmer is, the more verbose your programs have to be and the less accessible the languages are. Langages should definitely consider programmers as responsible persons.

So you don't have to use the 'create' option if you want an auto-creation and that could be called lazyness or responsability.

'sync' option

 'events' => {
     'clic' => {
         'sub'    => 'my_clic_sub',
         'thread' => 'My_thread_name',
         'sync'   => 'true',              # A 'true' value : the call will be synchronous
     },
 }

Well, the benefit of threads seems to be brought only by asynchronous calls, but there is a reason why you could wish a synchronous call. You may want to initialize data in your thread while changing values for the default event management. And the initialized data will be used after the default management in an asynchronous way. So you don't have to share variables between threads just because you want some events to be synchronous : variable 'scope' can then be limited. Read carefully the deadlock possibility if you use this option.

This point is easier to understand when you know that, for instance, for a single mouse clic, you can manage up to 3 different events.

Now what about the 'sync' option with a 'false' value ? You will force an asynchronous call and this could be used ... for the 'Graphic' thread ! This trick won't prevent you from freezing the user interface if your code is huge, but if you know what you are doing...

 'events' => {
     'clic' => {
         'sub'    => 'my_clic_sub',
         'sync'   => 'false',           # the 'Graphic' thread 
                                        # will execute 'my_clic_sub' asynchronously
     },
 }

The 'Graphic' thread asks a task to itself (puts it in its thread queue) but doesn't execute it immediately : first it has to end this event management. Once finished, it will execute its tasks in the order they have been queued... as well as manage other possible user events. Yes, the 'Graphic' thread is very active and it's very difficult to know when it will have a little time for you.

You would get the same result with this :

 'events' => {
     'clic' => {
         'sub'    => 'my_clic_sub',
         'thread' => 'Graphic',         # 'thread' option present => asynchronous call assumed
     },
 }

'sync', 'thread' and 'action' incompatibilities

When you work in an asynchronous way, the 'Graphic' thread doesn't wait for your answer. Then it can't receive a label for a 'jump' action or a return value for a 'change' action. So, only the 'exit' value of 'action' option is valid with asynchronous call.

deadlocks, 'pseudo' value for 'sync' option

When you use a thread in a synchronous way in an event management, you should understand what the 2 implied threads are doing :

  • the 'Graphic' thread is pending, waiting for your thread response

  • your thread is working hard, trying to slow down the least it can the 'Graphic' thread

So there is a very bad thing that could happen : if your thread asks for a service that is managed by the 'Graphic' thread... you know what follows... As the 'Graphic' thread is waiting for your answer (synchronous call), it can't serve your request so your thread waits endlessly for the 'Graphic' thread response and the 'Graphic' thread waits endlessly for your thread response. Everything is freezed forever (in fact some other threads are still working, but the 'Graphic' thread is the visual one and the only one versus the window manager).

So, synchronous calls initiated by the 'Graphic' thread can't use a 'Graphic' service. This is quite limiting but you could have to work with this limitation and find a solution : in the end, this is your job as a programmer.

Note that this is a general problem of multi-thread programmation : when a server thread asks another thread for a synchronous task, the executing thread can't use any service of the calling thread. The limitation is not linked to graphics.

tracking deadlock

That kind of deadlocks could be checked and will be managed like that :

  • a warning message, including the stack call, will be printed on STDERR

  • the executing thread will receive undef as an anwser : of course nothing will have been done by the initial calling server thread.

...but it's not yet managed. So in case you have a permanent freeze, you'll have to guess from which call the deadlock was introduced. Of course such a management will not be provided as a solution but as a help during development : this situation reveals a problem in conception.

'pseudo' value for 'sync' option

There is already a solution that could suit you : the 'Graphic' thread can make a 'pseudo-synchronous' call. It calls your thread asynchronously getting the 'call_id' (see Text::Editor::Easy::Comm). Then the 'Graphic' thread enters a loop where it checks 2 things at the same time :

  • Is there any task for me in the queue ? If true, then I execute it. These queued tasks do not include other user events coming from the window manager which will still remain pending.

  • Is the asynchronous call that I have asked for ended ? Which means : is your event sub ended ? If true, I exit the loop, get the answer and go on with the event management.

In order to have such a permissive management, you just have to use the 'pseudo' value for the 'sync' option :

 'events' => {
     'clic' => {
         'sub'    => 'my_clic_sub',
         'thread' => 'My_thread',
         'sync'   => 'pseudo',            # 'My_thread' can make calls to the 'Graphic' thread
         'action' => 'change',            # ... and can return a value to the 'Graphic' thread
     },
 }

So why should we keep the 'true' synchronous call ? Because with a pseudo-synchronous call, there is quite a big indetermination in the order of execution of the different tasks and maybe there is a chance that your deterministic program produces, sometimes, chaotic results. So 'pseudo' value for 'sync' option is provided but may lead, from time to time, to unexpected results, you are warned.

Note that chaotic reponses can be obtained with asynchronous calls too. Maybe a good thing to do is to change 'common data' thanks to synchronous calls and only. Asynchronous calls should only be used for displaying common data or changing private data (private to a thread). So using $editor->display or $line->select in an asynchronous called sub is OK but using $editor->insert or $line->set can lead to a race condition with unpredictible result, see "Thread Pitfalls: Races" in perlthrtut. In fact, 'Text::Editor::Easy' manages editor data in a private way (only 'File_manager' thread knows about the file being edited) but as methods can be called by any thread, these data should be considered as shared : if the cursor position is not the same when the event occurs as when you make your $editor->insert in your event sub (because an other thread have changed this position between the event and your asynchronous sub), the result may look funny (still worse if a delete has removed the line you were expecting to work on !).

DYNAMIC CONTRIBUTION

updating events and sequences

Suppose that your program have several 'Text::Editor::Easy' instances running. We've seen that you can add new instances with specific event management. Thanks to 'dynamic contribution', you can change event management of already running instances. A generalization of this 'dynamic contribution' is to have a default set of events used for future created instances.

The dynamic interface let you modify :

  • A single event management of a single instance (instance call)

  • All events of a single instance (instance call)

  • A single event of a all instances (class call)

  • All events of all instances (class call)

'Modify' event management should be understood as one of these possibilities :

  • Adding an new event (no management before)

  • Changing an old event

  • Deleting an old event

Moreover, as you can define your own sequences during instance creation, you can :

  • Change sequences for one instance or all instances

  • Delete a sequence (return to default one) for one or all instances.

set_event method

In order to change a single event, you have to use the 'set_event' method.

 Text::Editor::Easy->set_event( 
     'clic',                           # first parameter
     {                                 # second parameter, hash
         'sub'    => 'my_clic_sub',
         'thread' => 'My_thread',
     },
 };

In the previous example, the 'clic' event of 'all instances' (class call) will be changed.

The 'set_event' method accepts from 1 to 3 parameters :

  • The first is the name of the event to be changed

  • The second contains the information that should have been given during the instance creation : the interface is the same (you can create threads, eval a new module, ...).

    This second parameter can then be a hash reference (for a single action linked to this event) or an array reference (for multiple actions).

    If there is no second parameter (or an undef value), the event will be deleted.

  • A third optional parameter can add conditions to define if the event should be changed or not. These conditions should be used with class calls.

set_events method

The final 's' of 'set_events' method makes all the difference : here you redefine all the events at once (like 'events' option during instance creation)

 $editor->set_events( 
     {
         'clic', {
             'sub'    => 'my_clic_sub',
             'thread' => 'My_thread',
         },
         'motion', {
             'sub'    => 'my_motion_sub',
         },
     }
 };

In the previous example, all specific event management have been re-set for the existing instance $editor. Of course, only 'clic' and 'motion' events are defined here, but if the 'drag' or 'change' events were linked to specific subs, these old links are cut. If you want to keep an old specific management with 'set_events' method, you'll have to repeat it in order not to erase it.

The 'set_events' method accepts 1 parameter which exactly corresponds to the 'events' option used during the instance creation. For class call of 'set_events', an optional second parameter is possible.

Calling 'set_events' with no parameter (or an empty hash) will delete any specific event management.

set_sequence method

 # instance call
 $editor->set_sequence(
     {
         'clic' => [ 'my_action1', 'my_action2', '_set_cursor' ],
     }
 );
 
 # class call
 Text::Editor::Easy->set_sequence( 
     {
         'clic'   => [ 'my_action1', 'my_action2', '_set_cursor' ],
         'motion' => [ 'my_action2', 'motion', 'my_action3' ],
         'drag'   => [],
     },
     {
         'name'    => qr/\.pod$/,
     }
 );

You can change sequences after instance creation. There is no 's' at the end of 'set_sequence' : you can give more than one sequence to change in the first hash parameter but only the keys that you've given will be changed (different from 'set_events' principle).

Defining 'all instances', options for class calls

If you've read carefully the 2 previous examples, you already know that an instance call changes only one instance and a class call changes 'all instances'.

But 'all instances' is not very clear : only existing instances, only the ones that will be created from now, both, ... ?

Here, we're talking of class calls of 'set_event', 'set_events' and 'set_sequence' methods and we want to precise the subset of instances to which the changes will apply.

 # 'set_event' class call example with options
 
 Text::Editor::Easy->set_event( 
     'clic',                             # First parameter
     {                                   # Second parameter
         'sub'    => 'my_clic_sub',
         'thread' => 'My_thread',
     },
     {                                   # Third optional parameter
         'instances' => 'future',
         'values'    => 'undefined',
     }                                   # End of third parameter
 };
 
 
 # 'set_events' class call example with options
 
 Text::Editor::Easy->set_events( 
     { 'clic' =>                         # First parameter
         {
             'sub'    => 'my_clic_sub',
             'thread' => 'My_thread',
         },
     }
     {                                   # Second parameter
         'names' => qr/\.pl$/,           # 'Regexp' object
     }                                   # End of second parameter
 };

You can add a third parameter to 'set_event' method or a second parameter to 'set_events' and 'set_sequence' methods. This last parameter is an optional hash with the following keys :

  • 'instances' key, possible values are :

    • 'existing', will affect only existing instances but not the ones to come.

    • 'future', will affect only instances to come but not existing ones.

    • 'all', will affect all instances, this is the default option.

  • 'values' key, possible values are :

    • 'undefined', will affect only undefined event(s) : won't override existing management

    • 'defined', will affect only defined event(s) : replace existing management, but don't add management where there wasn't any.

    • 'all', no matter if event(s) were defined before : this is the default option

    With a 'set_event' call (that is, when you define a particular named event, for example, 'clic'), 'undefined' value means that, for each instance, a test will be done : if a 'clic' event already exists for the tested instance, it won't be updated.

    With a 'set_events' call (when you want to set all events at once), 'undefined' value means that there is not a single event managed in a specific way. You don't have used 'events' option during creation, or you have deleted all events afterwards.

  • 'names' key, this option accepts a 'Regexp' object that you can obtain with the qr// syntax. If the name of an 'Editor instance' matches the regexp, the change will apply.

'values' options with instance call

You can use the 'values' option with an instance call if you are too lazy to check what you've done before.

endless complexity with future instances

If you have made several class calls with 'set_event', 'set_events' and / or 'set_sequence' methods that affects new created instances, what will happen when a new instance will be created ? Which tests will be made, in which order ...?

The answer is : all tests will be done and in the order you have made the calls. As a joke, if you make calls from different threads, the order will be undefined !

This is a very complex mecanism of default event management. The interface to get all these default actions (done at each instance creation) or set or unset all these default actions in just one call follows.

events method

 my $events_ref = $editor->events;           # gets definition of all events in a single hash
                                             # (no parameter given)
 my $new_editor = Text::Editor::Easy->new;
 $new_editor->set_events( $events_ref );     # new editor with identical global event management
 
 
 my $event_ref = $editor->events('clic');    # gets definition of a single event (name given)
 my $new_editor = Text::Editor::Easy->new( {
     'events' => {
        'clic' => $event_ref,                # new editor with identical clic event management
     }
 } );

This method retrieves the event(s) definition of an instance (instance call). The first optional parameter is the name of an event.

With a class call, what you get is an array reference of the different force actions that are done during instance creation (ordered default event management with their options, according to class calls that you've made before : 'set_event', 'set_events' or 'set_sequence' class calls). For each element of the array, you've got :

  • the action to be done :

    • 'sequence' for a previous 'set_sequence' class method call

    • 'event' for a previous 'set_event' class method call

    • 'events' for a previous 'set_events' class method call

  • the parameters needed according to the method

sequences instance method

 my $hash_ref  = editor->sequences;                    # gets all specific sequences
 
 my $array_ref = editor->sequences('clic');            # gets 'clic' specific sequence

This instance method retrieves specific sequence(s) :

  • with no parameter, all specific sequences are returned in a hash reference

  • with a sequence name as first parameter, just this sequence is returned in an array reference

set_default class method

 my $default_ref = Text::Editor::Easy->events;       # gets all actions done by default
 
 Text::Editor::Easy->set_default( undef );           # deleting all default event management
 
 my $editor->Text::Editor::Easy->new;                # won't inherit former default event management
 
 Text::Editor::Easy->set_default( $default_ref );    # setting back default actions

You can make a list of 'set_event', 'set_events' and / or 'set_sequence' class calls that will be applied for future instances in just one call with 'set_default' method.

dynamic designing, 'code' option

Perl is dynamic : you can 'eval' new code during execution and in the context of the running program.

Suppose your program is (or contains) an editor, that sounds great ! Your program can ask you for new code to edit (or old one to change) and will go on running using this very code ! You can call that the way you want : a 'dynamic application', a limitless 'macro langage', the best configuration tool ever, or the most dangerous thing (it's true that powerful things put in bad hands are dangerous, but skilled people shouldn't be limited because of unskilled ones).

The 'code' option can replace 'sub', 'use' and 'package' options in standard and static event definition.

This option accepts a string that represents the code of the event.

Note that you must not start your code with 'sub { ...' : you should consider that you are already inside an unnamed sub.

 $editor->set_event( 
     'clic', { 
         'code' => 'print "Hello\n"';
     },
 );
 
 # is almost equivalent to
 
 $editor->set_event( 
     'clic', { 
         'sub' => 'hello';
     },
 );
 
 sub hello {
     print "Hello\n";
 }

About the differences :

  • to use the 'sub' option, you need a named sub written somewhere in your program or in a module whereas the 'code' option let you have your code in memory and nowhere else.

  • with the 'code' option, your code is 'checked' (or compiled) during the 'set_event' call (with an eval). Moreover, for each event, the code is executed in an 'eval'. So, the 'code' option is dynamic but slower : you have nothing without nothing ! But 'dynamic designing' should be considered as a faster way to design (as well as an extended responsability in the code : programmer and user; that is to say : an easier way to design) with a future possibility to migrate 'dynamic tested code' to 'static code'.

On the paper, this 'code' option seems useless because you have to write the code anyway (some furious programmers could think of 'computer generated' code, though). But if the code is written after you have started your application ... and by the user himself : see 'demo12' provided with the 'Editor.pl' program to understand.

PELL-MELL

Multiple subs for same event

You can link more than one sub to a single event. This can be interesting if you want to mix synchronous and asynchronous responses or just if you have 2 very different things to do and don't want to hide them in a bad named sub.

 my $editor = Text::Editor::Easy->new( 
     {                                          # start of editor new options
         'events' =>                            # 'events' option
         {
             'clic' =>                          # 'clic' event management
             [                                  # array reference : more than one sub possible
                 {
                     'sub' => 'my_clic_sub_1',  # first sub to execute in response to 'clic' event
                 },
                 {
                     'sub' => 'my_clic_sub_2',  # second sub to execute in response to 'clic' event
                 },
             ],                                 # end of clic event
             'motion' =>
             {                                  # hash reference : single sub management
                 'sub' => 'my_motion_sub',
             },
         }                                 # end of 'events' option
     }                                     # end of editor new options
 );                                        # end of new

The sub declaration order is very important in your array : subs are called in this order. So, if you use the 'action' option in the first event, other events could work with modified event information or could just be jumped.

Sending more data to your event sub

'execute_sequence' method

Sequences are powerful, so why should we limit them to event management ?

 $editor->execute_sequence( 
     [ '_show_editor', '_key_default', 'my_label' ],
     {
         'text' => "Text insertion without focus\n",
         'meta_hash' => {'alt' => 0, 'ctrl' => '0'}
     }
 );

This instance method needs at least one parameter :

  • first parameter, array reference (mandatory), contains the list of default actions (labels) or events you want to execute.

  • second parameter, hash reference (optional), should give information to the labels or events you have chosen (if they need any).

You could have done the first 2 actions of the example with the object interface :

 $editor->at_top;
 $editor->insert( "Text insertion without focus\n" );

saving event data

Suppose you've done complex things with the event management. Some events of a few instances are managed in a static way with subs written in different modules but other events are managed in a dynamic way with code in memory but saved nowhere...

This is a real mess but that costed you a lot to come to this ugly point and you wouldn't like to lose everything when your program will stop : either in a proper way or by a crash due to numerous bugs.

The session management should help you save everything of your instances in order to get the 'same' state that you've had before quitting.

'Text::Editor::Easy' module should be considered as a low-level module. It will never know anything about session. But there are enough possibilities to easily add this feature.

private keys

'events' options of the 'new' method and 'set_event(s)' method are insensitive to an addition of private keys :

 $editor->set_event( 
    'clic',
    {
        'sub'       => 'my_clic_sub',
        'duration'  => 'endless',       # private key, just ignored by 'set_event'
    }
 );

Limitations : the 2 following keys are used for internal optimisation and can't be private :

  • cid, compilation identification (for 'code' option)

  • tid, thread tid, linked to 'thread' option

'events' method, saving configuration

In order to save event management, you just have to call the 'events' method for each editor and test your private key :

 use Data::Dump qw( dump );                # To save a hash in a file
 my $event_ref = $editor->events;
 while ( my ( $key, $value_ref ) = each %$event_ref ) {
     my $duration = $value_ref->{'duration'};
     if ( ! defined $duration or $duration ne 'endless' ) {
        delete $event_ref->{$key};         # session event : not to be saved
    }
 }
 print CFG dump( $event_ref );             # CFG <=> 'config.file'

loading configuration

 my $event_ref = {};
 if ( -f 'config.file' ) {
     $event_ref = do( 'config.file' );     # gets hash back from file
 }
 my $editor = Text::Editor::Easy->new(
     {
         'events' => $event_ref,
         ...
     }
 );

warning

You should only use names for editor instances and threads if you plan to save configuration. tids (thread identification) or references (address of memory location) have almost no chance to remain the same after a restart.

defining your own label (own default action)

You can create sequences as you wish (static ones or dynamic ones). In these sequences, every label that does not start with '_' is considered as an event or a private label.

creating a private label ('set_event' method)

 # Instance label (known by only one instance)
 $editor->set_event( 'my_action',                    # label 'my_action'
     {
         'sub'    => 'my_sub',
         'action' =>  'change,
     }
 }
 
 # Class label (known by all instances)
 Text::Editor::Easy->set_event( 'my_class_action',   # label 'my_class_action'
     {
         'sub'    => 'my_other_sub',
         'thread' =>  'Foo',
     }
 }

using a private label

All you have to do is to define a sequence that contains one of your labels :

 $editor->set_sequence( {
    'shift_motion' => [ '_show_editor', 'my_action', 'my_class_action' ],
} );

The true event 'shift_motion' will then execute a predefined label (default action '_show_editor') and your 2 private labels (these 2 newly defined labels can also be called : "generated events that were not predefined").

simulating events...

Events can be simulated with method calls.

Here are examples :

 # true clic simulation
 $editor->clic( {'x' => 27, 'y' => 5 } );
 
 # true 'shift_motion' simulation
 $editor->motion( {'x' => 27, 'y' => 5, 'meta' => 'shift_' } );
 
 # true 'ctrl_drag' simulation
 $editor->drag( {'x' => 27, 'y' => 5, 'meta' => 'ctrl_' } );

Here are the usable methods :

  • 'key',

  • 'clic',

  • 'motion',

  • 'drag',

  • 'wheel',

  • 'double_clic',

  • 'right_clic',

There is a little difference in the information that you get. The 'caller' key won't be starting with a 'U_' but with an integer (the tid of the thread that made the simulation event call).

...and forcing the sequence

All the previous methods accepts one parameter (the information hash) and an optional sequence.

 # true clic simulation with a private sequence (just for this simulated event)
 $editor->clic( {'x' => 27, 'y' => 5 }, [ '_set_cursor', 'my_foo_clic' ] );

This sequence is 'stronger' than any other (default or overloaded) but doesn't last after the method call.

EVENT LIST

For each 'true event', sequences are explained.

For each event, the hash information you receive as second parameter in your sub is explained.

Labels defining a default action are explained here.

Lots of events are not yet managed ('save', 'resize', 'destroy', 'lost_focus', 'get_focus', ...).

general prefixes

Thanks to modifier keys, events can be very numerous. See modifier keys for explanations.

Here are the 9 usable prefixes with most events :

  • no prefix ('' = empty string = q{})

  • any_

  • alt_

  • ctrl_

  • shift_

  • alt_ctrl_

  • alt_shift_

  • ctrl_shift_

  • alt_ctrl_shift_ (applications designed for pianists only !)

general keys of info_ref hash

Whatever the event, the second parameter of your event sub is a hash containing, at least, the following keys :

  • true, name of the 'true event' that has generated the sequence

  • label, name of the current label that is executed (generated event)

  • meta, string containing the modifier key combination ('', 'shift_', 'alt_ctrl_', ...)

  • meta_hash, hash containing the keys 'alt', 'ctrl' and 'shift' : values are true if the corresponding modifier key was pressed.

  • caller, 'event id' ('U_\d+') or 'call_id' if the event is simulated by a call

'meta_hash' and 'meta' can be interesting for 'any_.*' events :

 my $editor = Text::Editor::Easy->new( 
    {
        'file'   => 'my_file.t3d',
        'events' => {
            'any_clic' => {                   # clic for any modifier key combination
                'sub'    => 'my_clic_sub',
            },                         
        }
    }
 );
 
 [...]
 
 sub my_clic_sub {
     my ( $editor, $info_ref ) = @_;
 
     if ( $info_ref->{'meta_hash'}{'alt'} ) {
         print "You pressed the alt key during the clic\n";
     }
     print "Meta combination string is ", $info_ref->{'meta'}, "\n";
 }

clic subset

There are 27 predefined 'clic' event labels (3 x 9) given the 3 following suffixes :

  • clic

  • right_clic

  • double_clic

Here are examples of event names that you could use as keys of the 'events' hash during editor instance creation :

 clic
 ctrl_clic
 alt_shift_right_clic
 any_right_clic
 alt_ctrl_shift_double_clic   # for a hidden functionality !

There are also 3 more 'clic' events :

  • 'any_any_clic', event that happens whatever the 'clic' (simple, double or right) and whatever the meta-keys pressed.

  • 'after_clic', happens after the default management of a clic sequence

  • 'any_after_clic', shouldn't be very useful...

information received in clic subset events

The information received by your sub is a hash containing the following keys :

  • x, abscisse of the clic

  • y, ordinate of the clic

  • line, line instance receiving the clic

  • pos, position of the clic in characters in the line instance (from the left)

'clic' sequence

The sequence depends on the meta-keys. With no meta-keys pressed :

  • _calc_line_pos, default action

  • any_any_clic, generated event

  • any_clic, generated event

  • clic, generated event

  • _test_resize, default action

  • _set_cursor, default action

  • any_after_clic, generated event

  • after_clic, generated event

With meta-keys pressed, here is the sequence :

  • _calc_line_pos, default action

  • any_any_clic, generated event

  • any_clic, generated event

  • ${meta}clic, generated event

  • any_after_clic, generated event

Scalar $meta represents one of the following 7 possibilities :

 'alt_'
 'ctrl_'
 'shift_'
 'alt_ctrl_'
 'alt_shift_'
 'ctrl_shift'
 'alt_ctrl_shift'

As you can see, there are no default action if a meta-key is pressed ('_calc_line_pos' adds just 'pos' and 'line' keys to the information hash).

'right_clic' sequence

There is just one sequence (no real default actions) :

  • _calc_line_pos, default action

  • any_any_clic, generated event

  • any_right_clic, generated event

  • ${meta}right_clic, generated event

$meta represents one of the following 8 possibilities :

 ''
 'alt_'
 'ctrl_'
 'shift_'
 'alt_ctrl_'
 'alt_shift_'
 'ctrl_shift'
 'alt_ctrl_shift'

'double_clic' sequence

There is just one sequence (no real default actions) :

  • _calc_line_pos, default action

  • any_any_clic, generated event

  • any_double_clic, generated event

  • ${meta}double_clic, generated event

Be careful, a double clic is... 2 clics !

  • The first clic generates a 'true clic' event : this is quite normal because for your ultra fast computer, there is very long delay between your 2 clics, and as it can't guess the future, it generates a clic event.

  • Then the second clic is interpreted as a 'double clic' (according to the maximum delay you've configured in your window manager, or, rather, mouse manager).

motion subset

motion information

The information received by your sub is a hash containing the same keys as clic events :

  • x, abscisse of the motion (mouse cursor)

  • y, ordinate of the motion (mouse cursor)

  • line, line instance under the mouse cursor

  • pos, position of the mouse cursor in characters in the line instance (from the left)

motion sequence

In fact, it's said in the introduction that the motion event is not managed by default, and this is wrong.

Without meta-keys pressed, here is the sequence :

  • _calc_line_pos, default action

  • any_motion, generated event

  • motion, generated event

  • _show_editor, default action

  • _update_cursor, default action

  • any_after_motion, generated event

  • after_motion, generated event

With, meta-keys, it's true that there is no default management :

  • _calc_line_pos, default action

  • any_motion, generated event

  • ${meta}motion, generated event

  • any_after_motion, generated event

So you have a possible of 11 motion events.

drag subset

drag information

  • shape, mouse cursor shape (double arrow for a resize command or simple arrow for a select command)

  • x, abscisse of the mouse cursor

  • y, ordinate of the mouse cursor

  • line, line instance under the mouse cursor

  • pos, position of the mouse cursor in characters in the line instance (from the left)

drag sequence

As there are default actions for the drag, there are 2 different sequences according to meta-keys. With no meta-keys pressed, the sequence is :

  • _calc_line_pos, default action

  • any_drag, generated event

  • drag, generated event

  • _zone_resize, default action

  • _drag_select, default action

  • any_after_drag, generated event

  • after_drag, generated event

With any meta-keys, the sequence is :

  • _calc_line_pos, default action

  • any_drag, generated event

  • ${meta}drag, generated event

  • any_after_drag, generated event

key subset

key information

Don't expect too much about these informations. If there are all right, it's just that you have an american keyboard.

  • text, this is the printable information. With some keys (like backspace) you shouldn't consider this information valid.

  • key, should give the 'key code'. For my french keyboard, all 'AltGr' keys are wrong (#, {, [, |, \, ...)

  • ascii, should give the ascii code.

  • uni, should give the unicode.

  • key_code, concatenation of $meta and 'key' key (for instance 'alt_ctrl_b', or 'ctrl_space'). Almost identical to general 'true' key ('true event' name): 'true' key has an additionnal '_key' suffix in the end.

key sequence

  • any_any_key, generated event

  • any_${key}_key, generated event

  • ${key_code}_key, generated event

  • _key_code, default action

  • _key_default, default action

'any_any_key' happens whatever the key pressed and whatever meta_keys (similar to 'any_any_clic').

wheel subset

wheel information

  • unit, represents how much the user have rolled the wheel mouse (can be negative)

  • move, represents the number of pixels (or lines in future console mode) the editor will be moved to (the move is only vertical)

wheel sequence

There are 2 sequences depending on meta-keys. With no meta-key pressed, the sequence is :

  • any_wheel, generated event

  • wheel, generated event

  • _wheel_move, default action

  • after_wheel, generated event

With any meta-key pressed, the sequence is :

  • any_wheel, generated event

  • ${meta}wheel, generated event

change event

At present, this is rather an 'after_change' because it happens once changes have been applied. This is just a start. Meta-keys can't be used to create numerous 'change' events.

No sequence is done (will belong to other sequences : it is not a true event).

cursor_set event

As change event, this is rather an 'after_cursor_set' event. Meta-keys have no sense too.

No sequence is done (will belong to other sequences : it is not a true event).

LABEL DETAILS (DEFAULT ACTIONS)

All these actions can be used in your specific sequences. Every label that does not start with a '_' is considered as a generated event (= private label).

For each default action, keys needed in the information hash are explained as well as keys added.

_calc_line_pos

This sub needs 'x' and 'y' keys.

Adds 'line' and 'pos' keys to the information hash.

This label could be used in a user sequence where an event changes 'x' and 'y' coordinates and wants to have 'line' and 'pos' keys coherent with the new 'x' and 'y'.

_test_resize

This sub needs 'x' and 'y' keys.

Adds 'resize' key.

If the cursor has a double arrow shape, then the clic is considered as a resize command (rather than a "set cursor" one) : in this case, the 'resize' key is added to the information hash with a true value (value is 1) and '_set_cursor' label is jumped (label is set to 'any_after_clic' in case there are 'after_clic' events).

If the clic is not considered as a resize command, then the 'resize' key is still added but with a false value (value is 0). Of course, there is no jump.

_set_cursor

This sub needs 'pos' and 'line' keys.

4 little actions are done :

  • editon cursor is set accordinly

  • focus is given to the editor

  • if there was selected text, it's un-selected

  • a little adjustment of the editor look is possible (when cursor is too close to a border)

_update_cursor

Needs 'x' and 'y' keys.

Sets mouse cursor shape according to its position versus the editor borders (in order to initiate a resize command, double arrow, or a cursor set command, simple arrow).

_show_editor

This sub does not need any key.

It places the editor on top of all the other ones (in case, some parts of the editor is under other ones).

_zone_resize

Needs keys 'shape', 'x' and 'y'.

According to 'shape' key, resizes editor.

_drag_select

Needs 'line' and 'pos' keys.

Selects text.

_wheel_move

Needs 'move' key.

Scrolls the editor.

_key_code

Needs 'key_code' key.

There are in fact 2 different systems of event management for key pressed events. The general one, using 'events' option (or 'set_event(s)' methods) and another one using method 'bind_key' (with a class call, for a global binding, or an instance call).

Threads and code are usable only with the general event management. With 'bind_key' method, you can use 'sub_ref' option (a sub reference) but you have to call the 'bind_key' method from the thread with tid 0 : 'bind_key' user subs are always executed by thread 0.

Maybe these 2 systems of key event management will go on living together...

The '_key_code' action is just a call to the good 'bind_key' sub : first, a specific (to the instance) 'bind_key' sub is looked for. If no specific binding is found, a global binding is looked for.

_key_default

Needs 'text' and 'meta_hash' keys.

Insert 'text' provided that no 'ctrl' or 'alt' key is pressed.

_exit

No key needed.

Could be used in a dynamic sequence (in order to avoid other following actions).

_jump

'jump' key needed.

Could be used in a dynamic sequence to make a dynamic jump according to previous actions. (The 'jump' value is just the next label to be done, see jump action).

EVENTS LINKED TO A ZONE INSTANCE

Events can be linked to a 'zone' instance rather than to a 'Text::Editor::Easy' instance.

2 events are acessible for a 'zone' instance :

  • top_editor_change : happens each time a new 'editor' instance is on top of the zone.

  • editor_destroy : happens each time a 'editor' instance belonging to the zone is destroyed.

CONCLUSION

Maybe the interface seems a little complex in the end, still complexity have not been added freely. If this interface is adapted to programs that change, then the goal will be reached :

  • first, you use synchronous events executed by the 'Graphic' thread

  • second, your application is growing (and your event code too), becoming more interesting and ... slower

  • Then you add threads where you feel it would help, keeping your code and your declarations but adding a few 'thread' options and and a few return if anything_for_me; instructions.

COPYRIGHT & LICENSE

Copyright 2008 - 2009 Sebastien Grommier, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.