-
Notifications
You must be signed in to change notification settings - Fork 703
Framework Design Guidelines
This project has become confused on whether the idiom for events is based on Action
or event/EventHandler
. In addition there's gross inconsistency between naming of events. E.g.
- Recently we added static public Action OnResized to Application.
- Meanwhile most View derived events use event as in public event EventHandler Enter.
- Some event handlers use "On" and some don't. E.g. public event EventHandler OnOpenMenu vs. public event EventHandler TextChanged.
We are not consistent along these lines in Terminal.Gui at all. This leads to friction for adopters and bugs.
- See https://github.com/migueldeicaza/gui.cs/issues/447
- I found this article to be especially clarifying: https://www.codeproject.com/Articles/20550/C-Event-Implementation-Fundamentals-Best-Practices
@migueldeicaza is pretty clear on his preference for Action
and dislike of event/EventHandler
. Based on this, here is a PROPOSAL for adjusted "rules" for this project w.r.t. events.
The best guidance on naming event related things I've seen is this: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/names-of-type-members?redirectedfrom=MSDN
Proposed Event rules for Terminal.Gui
moving forward (STILL UP FOR DEBATE):
- We follow the naming advice provided in https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/names-of-type-members?redirectedfrom=MSDN. Adaptaions specifically to
Terminal.Gui
:- To make it clear that the
Action
is being used for event semantics, event names should use the wordEvent
in them and notAction
. For use cases where the delgate does not have eventing semantics, usingAction
(or something else appropriate) is ok.
- To make it clear that the
- We use the
Action<T>
idiom primarily. We do not use theevent/EventHandler
idiom. - The class that defines the event and can raise the event will implement:
- An event based on
Action<T>
, as inpublic Action<EventToRaiseArgs> EventToRaise
-
T
can be any type, and designers are encouraged to be thoughtful about how subscribers will utilize the state.- If there is any chance that additional state may be needed to be passed with the event in the future,
T
should be a type defined along with the class (e.g.KeyEventArgs
has members forKeyEvent
, andHandled
). - Generally, previous state is more valuable to a subscriber than new state (subscribers can usually just ask the object for the new state). For this reason, passing "previous values" is encouraged where appropriate.
- Don't over do it. If there is not a clear/real use-case it is better to not over-engineer (but see the point above about making
T
a class that can more easily be extended in the future, minimizing subscriber changes required).
- If there is any chance that additional state may be needed to be passed with the event in the future,
- A
virtual
event raising function, named asOnEventToRaise
. Typical implementations will simply do aEventToRaise?.Invoke(eventToRaiseArgs)
. This enables sub-classes to change the event raising behavior, an important extensibility mechanism. - Subscribers of the event can do
theobject.EventToRaise+= (args) => {};
to subscribe. - Sub-classes of the class implementing
EventToRaise
can overrideOnEventToRaise
as needed.
- An event based on
UPDATE THIS AS THESE GET FIXED
-
ConsoleDriver
- Init - Uses Action
- Fine - Internal API
- PepareToRun - Uses Action -
- Fine - internal API
-
protected Action TerminalResized;
- Fine - Internal API
- Init - Uses Action
-
NetMainLoop -
- Uses Action for
public Action<ConsoleKeyInfo> WindowsKeyPressed;
- FIX: This should not be
public
!?!?
- FIX: This should not be
- Uses Action for
-
Application
-
public static Action<MouseEvent> RootMouseEvent;
- Fine - Debugging API
- FIXED:
public static Action<ResizedEventArgs> Loaded
- FIXED:
public static Action<ResizedEventArgs> Resized
-
-
MainLoop
-
public void Invoke (Action action)
- Fine; threading related. Action works great with Tasks.
-
-
View
- ALL FIXED:
public Action<FocusEventArgs> Enter;
etcc..
- ALL FIXED:
-
TopLevel
- FIXED
-
Button
- FIXED
-
Checkbox
- FIXED
-
ComboBox
- FIXED?
-
Menu
- FIXED
-
RadioGroup
- FIXED:
public Action<SelectedItemChangedArgs> SelectedItemChanged;
- FIXED:
-
ScrollView
- FINE.
-
StatusBar
- FINE.
public Action Action { get; }
- FINE.
-
FileDialog
-
public Action<ustring> DirectoryChanged { get; set; }
- SHOULD FIX - DirectorySelected`?
-
public Action<ustring> FileChanged { get; set; }
- SHOULD FIX:
FileSelected
- SHOULD FIX:
-
public Action<(string, bool)> SelectedChanged { get; set; }
- SHOULD FIX:
SelectedFileChanged
???
- SHOULD FIX:
-
-
ListView
- SHOULD FIX:
OnSelectedChanged
->OnSelectedItemChanged
- SHOULD FIX:
-
TextView
-
public Action TextChanged;
- BAD DESIGN
- Every time
Text
text changes, a copy of the ENTIRE buffer will be made and passed with this event. - For classes that deal with small amounts of data, this design would be fine. But
TextView
may hold megabytes or more. - Ideally a
EventArgs
subclass would be deinfed to make it explicitly clear that theustring
is the old value AND make it easier to add more state in the future. - Instead, we should make an exception to sending state with the event and not do it.
- Should this class should be redesigned to use reference values for the text buffer, vs naively just causing copy operations?
- Every time
- BAD DESIGN
-
-
TextField
-
public Action<ustring> TextChanged;
- Inconsistent relative to same property on
TextView
- BAD DESIGN
- Every time
Text
text changes, a copy of the ENTIRE buffer will be made and passed with this event. - For classes that deal with small amounts of data, this design would be fine. But
TextView
may hold megabytes or more. - Instead, we should make an exception to sending state with the event and not do it.
- Should this class should be redesigned to use reference values for the text buffer, vs naively just causing copy operations?
- Every time
- Inconsistent relative to same property on
-