Fixes #3209 - Formalize Cancellable Work Pattern and add helpers (#4092)

This commit is contained in:
Tig
2025-06-03 08:12:57 -06:00
committed by GitHub
parent 7490ac9776
commit 764a804ddd
127 changed files with 3720 additions and 1421 deletions

View File

@@ -34,6 +34,8 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
csharp_style_var_elsewhere = false:suggestion
csharp_style_var_when_type_is_apparent = false:suggestion
dotnet_diagnostic.bc40000.severity = warning
dotnet_diagnostic.bc400005.severity = warning
dotnet_diagnostic.bc40008.severity = warning
@@ -545,7 +547,7 @@ resharper_formatter_tags_enabled = false
resharper_format_leading_spaces_decl = false
resharper_for_built_in_types = use_var_when_evident
resharper_for_other_types = use_explicit_type
resharper_for_simple_types = use_var_when_evident
resharper_for_simple_types = use_explicit_type
resharper_ignore_space_preservation = false
resharper_include_prefix_comment_in_indent = false
resharper_indent_anonymous_method_block = true
@@ -864,7 +866,7 @@ resharper_arrange_default_value_when_type_evident_highlighting = suggestion
resharper_arrange_default_value_when_type_not_evident_highlighting = suggestion
resharper_arrange_local_function_body_highlighting = warning
resharper_arrange_method_or_operator_body_highlighting = hint
resharper_arrange_null_checking_pattern_highlighting = error
resharper_arrange_null_checking_pattern_highlighting = hint
resharper_arrange_object_creation_when_type_evident_highlighting = suggestion
resharper_arrange_object_creation_when_type_not_evident_highlighting = warning
resharper_arrange_redundant_parentheses_highlighting = warning

View File

@@ -10,8 +10,8 @@ We welcome contributions from the community. See [Issues](https://github.com/gui
Terminal.Gui uses the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branching model.
* The `v1_release_` and `v2_release` branches are always stable, and always matches the most recently released Nuget package.
* The `v1__develop` and `v2_develop` branches are where new development and bug-fixes happen. `v2_develop` is the default Github branch.
* The `v1_release` and `v2_release` branches are always stable, and always match the most recently released Nuget package.
* The `v1_develop` and `v2_develop` branches are where new development and bug-fixes happen. `v2_develop` is the default Github branch.
### Forking Terminal.Gui
@@ -141,34 +141,8 @@ Great care has been provided thus far in ensuring **Terminal.Gui** has great [AP
### Defining Events
The [Microsoft .NET Framework Design Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/) provides these guidelines for defining events:
See https://gui-cs.github.io/Terminal.GuiV2Docs/docs/events.html
> Events always refer to some action, either one that is happening or one that has occurred. Therefore, as with methods, events are named with verbs, and verb tense is used to indicate the time when the event is raised.
>
> ✔️ DO name events with a verb or a verb phrase.
>
> Examples include Clicked, Painting, DroppedDown, and so on.
>
> ✔️ DO give events names with a concept of before and after, using the present and past tenses.
>
> For example, a close event that is raised before a window is closed would be called Closing, and one that is raised after the window is closed would be called Closed.
>
> ❌ DO NOT use "Before" or "After" prefixes or postfixes to indicate pre- and post-events. Use present and past tenses as just described.
>
> ✔️ DO name event handlers (delegates used as types of events) with the "EventHandler" suffix, as shown in the following example:
>
> ✔️ DO name event argument classes with the "EventArgs" suffix.
1. We follow the naming guidelines provided in https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/names-of-type-members?redirectedfrom=MSDN
2. We use the `event EventHandler<T>` idiom.
3. For public APIs, the class that can raise the event will implement:
- A `virtual` event raising function, named as `OnEventToRaise`. Typical implementations will simply do a `EventToRaise?.Invoke(this, eventArgs)`.
- An `event` as in `public event EventHandler<EventArgs> EventToRaise`
- Consumers of the event can do `theobject.EventToRaise += (sender, args) => {};`
- Sub-classes of the class implementing `EventToRaise` can override `OnEventToRaise` as needed.
4. Where possible, a subclass of `EventArgs` should be provided and the old and new state should be included. By doing this, event handler methods do not have to query the sender for state.
See also: https://www.codeproject.com../docs/20550/C-Event-Implementation-Fundamentals-Best-Practices
### Defining new `View` classes

View File

@@ -136,7 +136,7 @@
{
"UI Catalog Theme": {
"Window.DefaultShadow": "Transparent",
"CheckBox.DefaultHighlightStyle": "Hover, Pressed, PressedOutside",
"CheckBox.DefaultHighlightStates": "In, Pressed, PressedOutside",
"MessageBox.DefaultButtonAlignment": "Start",
"StatusBar.DefaultSeparatorLineStyle": "Single",
"Dialog.DefaultMinimumWidth": 80,
@@ -149,7 +149,7 @@
"Button.DefaultShadow": "Transparent",
"FrameView.DefaultBorderStyle": "Double",
"MessageBox.DefaultMinimumHeight": 0,
"Button.DefaultHighlightStyle": "Hover, Pressed",
"Button.DefaultHighlightStates": "In, Pressed",
"Menuv2.DefaultBorderStyle": "Heavy",
"MenuBarv2.DefaultBorderStyle": "Heavy",
"Schemes": [

View File

@@ -181,7 +181,7 @@ public class Scenario : IDisposable
private void OnApplicationOnInitializedChanged (object? s, EventArgs<bool> a)
{
if (a.CurrentValue)
if (a.Value)
{
lock (_timeoutLock!)
{
@@ -196,7 +196,7 @@ public class Scenario : IDisposable
cd.Refreshed += (sender, args) =>
{
BenchmarkResults.RefreshedCount++;
if (args.CurrentValue)
if (args.Value)
{
BenchmarkResults.UpdatedCount++;
}

View File

@@ -51,7 +51,7 @@ public class Adornments : Scenario
{
Normal = new (
color.SuperView.GetAttributeForRole (VisualRole.Normal).Foreground,
e.CurrentValue,
e.Result,
color.SuperView.GetAttributeForRole (VisualRole.Normal).Style
)
});
@@ -130,7 +130,7 @@ public class Adornments : Scenario
Y = 1,
Text = "_Button in Padding Y = 1",
CanFocus = true,
HighlightStyle = HighlightStyle.None,
HighlightStates = MouseState.None,
};
btnButtonInPadding.Accepting += (s, e) => MessageBox.Query (20, 7, "Hi", "Button in Padding Pressed!", "Ok");
btnButtonInPadding.BorderStyle = LineStyle.Dashed;

View File

@@ -223,9 +223,9 @@ public class Arrangement : Scenario
return;
void ColorPickerColorChanged (object sender, ColorEventArgs e)
void ColorPickerColorChanged (object sender, ResultEventArgs<Color> e)
{
testFrame.SetScheme (testFrame.GetScheme () with { Normal = new (testFrame.GetAttributeForRole (VisualRole.Normal).Foreground, e.CurrentValue) });
testFrame.SetScheme (testFrame.GetScheme () with { Normal = new (testFrame.GetAttributeForRole (VisualRole.Normal).Foreground, e.Result) });
}
}

View File

@@ -415,7 +415,7 @@ public class Bars : Scenario
Title = "_File",
HelpText = "File Menu",
Key = Key.D0.WithAlt,
HighlightStyle = HighlightStyle.Hover
HighlightStates = MouseState.In
};
var editMenuBarItem = new Shortcut
@@ -423,7 +423,7 @@ public class Bars : Scenario
Title = "_Edit",
HelpText = "Edit Menu",
Key = Key.D1.WithAlt,
HighlightStyle = HighlightStyle.Hover
HighlightStates = MouseState.In
};
var helpMenuBarItem = new Shortcut
@@ -431,7 +431,7 @@ public class Bars : Scenario
Title = "_Help",
HelpText = "Halp Menu",
Key = Key.D2.WithAlt,
HighlightStyle = HighlightStyle.Hover
HighlightStates = MouseState.In
};
bar.Add (fileMenuBarItem, editMenuBarItem, helpMenuBarItem);
@@ -445,7 +445,7 @@ public class Bars : Scenario
Title = "Z_igzag",
Key = Key.I.WithCtrl,
Text = "Gonna zig zag",
HighlightStyle = HighlightStyle.Hover
HighlightStates = MouseState.In
};
var shortcut2 = new Shortcut
@@ -453,7 +453,7 @@ public class Bars : Scenario
Title = "Za_G",
Text = "Gonna zag",
Key = Key.G.WithAlt,
HighlightStyle = HighlightStyle.Hover
HighlightStates = MouseState.In
};
var shortcut3 = new Shortcut
@@ -461,7 +461,7 @@ public class Bars : Scenario
Title = "_Three",
Text = "The 3rd item",
Key = Key.D3.WithAlt,
HighlightStyle = HighlightStyle.Hover
HighlightStates = MouseState.In
};
var line = new Line ()
@@ -475,13 +475,13 @@ public class Bars : Scenario
Title = "_Four",
Text = "Below the line",
Key = Key.D3.WithAlt,
HighlightStyle = HighlightStyle.Hover
HighlightStates = MouseState.In
};
shortcut4.CommandView = new CheckBox ()
{
Title = shortcut4.Title,
HighlightStyle = HighlightStyle.None,
HighlightStates = MouseState.None,
CanFocus = false
};
// This ensures the checkbox state toggles when the hotkey of Title is pressed.

View File

@@ -415,6 +415,7 @@ public class Buttons : Scenario
var repeatButton = new Button
{
Id = "repeatButton",
X = Pos.Right (label) + 1,
Y = Pos.Top (label),
Title = $"Accept Co_unt: {acceptCount}",

View File

@@ -68,13 +68,13 @@ public class CharacterMap : Scenario
_charMap.SelectedCodePointChanged += (sender, args) =>
{
if (Rune.IsValid (args.CurrentValue))
if (Rune.IsValid (args.Value))
{
jumpEdit.Text = ((Rune)args.CurrentValue).ToString ();
jumpEdit.Text = ((Rune)args.Value).ToString ();
}
else
{
jumpEdit.Text = $"U+{args.CurrentValue:x5}";
jumpEdit.Text = $"U+{args.Value:x5}";
}
};

View File

@@ -190,9 +190,9 @@ public class ColorPickers : Scenario
cbShowTextFields.CheckedStateChanging += (_, e) =>
{
foregroundColorPicker.Style.ShowTextFields = e.NewValue == CheckState.Checked;
foregroundColorPicker.Style.ShowTextFields = e.Result == CheckState.Checked;
foregroundColorPicker.ApplyStyleChanges ();
backgroundColorPicker.Style.ShowTextFields = e.NewValue == CheckState.Checked;
backgroundColorPicker.Style.ShowTextFields = e.Result == CheckState.Checked;
backgroundColorPicker.ApplyStyleChanges ();
};
app.Add (cbShowTextFields);
@@ -209,9 +209,9 @@ public class ColorPickers : Scenario
cbShowName.CheckedStateChanging += (_, e) =>
{
foregroundColorPicker.Style.ShowColorName = e.NewValue == CheckState.Checked;
foregroundColorPicker.Style.ShowColorName = e.Result == CheckState.Checked;
foregroundColorPicker.ApplyStyleChanges ();
backgroundColorPicker.Style.ShowColorName = e.NewValue == CheckState.Checked;
backgroundColorPicker.Style.ShowColorName = e.Result == CheckState.Checked;
backgroundColorPicker.ApplyStyleChanges ();
};
app.Add (cbShowName);
@@ -226,7 +226,7 @@ public class ColorPickers : Scenario
}
/// <summary>Fired when background color is changed.</summary>
private void BackgroundColor_ColorChanged (object sender, EventArgs e)
private void BackgroundColor_ColorChanged (object sender, ResultEventArgs<Color> e)
{
UpdateColorLabel (_backgroundColorLabel,
backgroundColorPicker.Visible ?
@@ -237,7 +237,7 @@ public class ColorPickers : Scenario
}
/// <summary>Fired when foreground color is changed.</summary>
private void ForegroundColor_ColorChanged (object sender, EventArgs e)
private void ForegroundColor_ColorChanged (object sender, ResultEventArgs<Color> e)
{
UpdateColorLabel (_foregroundColorLabel,
foregroundColorPicker.Visible ?

View File

@@ -142,9 +142,9 @@ public class DynamicMenuBar : Scenario
TextHotKey.TextChanging += (s, e) =>
{
if (!string.IsNullOrEmpty (e.NewValue) && char.IsLower (e.NewValue [0]))
if (!string.IsNullOrEmpty (e.Result) && char.IsLower (e.Result [0]))
{
e.NewValue = e.NewValue.ToUpper ();
e.Result = e.Result.ToUpper ();
}
};
TextHotKey.TextChanged += (s, _) => TextHotKey.SelectAll ();
@@ -208,20 +208,20 @@ public class DynamicMenuBar : Scenario
CkbIsTopLevel.CheckedStateChanging += (s, e) =>
{
if ((_menuItem != null && _menuItem.Parent != null && e.NewValue == CheckState.Checked)
|| (_menuItem == null && _hasParent && e.NewValue == CheckState.Checked))
if ((_menuItem != null && _menuItem.Parent != null && e.Result == CheckState.Checked)
|| (_menuItem == null && _hasParent && e.Result == CheckState.Checked))
{
MessageBox.ErrorQuery (
"Invalid IsTopLevel",
"Only menu bar can have top level menu item!",
"Ok"
);
e.Cancel = true;
e.Handled = true;
return;
}
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
CkbSubMenu.CheckedState = CheckState.UnChecked;
CkbSubMenu.SetNeedsDraw ();
@@ -243,13 +243,13 @@ public class DynamicMenuBar : Scenario
TextAction.Text = "";
TextShortcutKey.Enabled =
e.NewValue == CheckState.Checked && CkbSubMenu.CheckedState == CheckState.UnChecked;
e.Result == CheckState.Checked && CkbSubMenu.CheckedState == CheckState.UnChecked;
}
};
CkbSubMenu.CheckedStateChanged += (s, e) =>
{
if (e.CurrentValue == CheckState.Checked)
if (e.Value == CheckState.Checked)
{
CkbIsTopLevel.CheckedState = CheckState.UnChecked;
CkbIsTopLevel.SetNeedsDraw ();
@@ -275,7 +275,7 @@ public class DynamicMenuBar : Scenario
if (_hasParent)
{
TextShortcutKey.Enabled = CkbIsTopLevel.CheckedState == CheckState.UnChecked
&& e.CurrentValue == CheckState.UnChecked;
&& e.Value == CheckState.UnChecked;
}
}
};
@@ -284,7 +284,7 @@ public class DynamicMenuBar : Scenario
{
if (_menuItem != null)
{
_menuItem.AllowNullChecked = e.CurrentValue == CheckState.Checked;
_menuItem.AllowNullChecked = e.Value == CheckState.Checked;
}
};
@@ -792,13 +792,13 @@ public class DynamicMenuBar : Scenario
txtDelimiter.TextChanging += (s, e) =>
{
if (!string.IsNullOrEmpty (e.NewValue))
if (!string.IsNullOrEmpty (e.Result))
{
Key.Separator = e.NewValue.ToRunes () [0];
Key.Separator = e.Result.ToRunes () [0];
}
else
{
e.Cancel = true;
e.Handled = true;
txtDelimiter.SelectAll ();
}
};

View File

@@ -901,14 +901,14 @@ public class Editor : Scenario
{
X = 0, Y = Pos.Top (txtToFind) + 2, CheckedState = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase"
};
ckbMatchCase.CheckedStateChanging += (s, e) => _matchCase = e.NewValue == CheckState.Checked;
ckbMatchCase.CheckedStateChanging += (s, e) => _matchCase = e.Result == CheckState.Checked;
d.Add (ckbMatchCase);
var ckbMatchWholeWord = new CheckBox
{
X = 0, Y = Pos.Top (ckbMatchCase) + 1, CheckedState = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word"
};
ckbMatchWholeWord.CheckedStateChanging += (s, e) => _matchWholeWord = e.NewValue == CheckState.Checked;
ckbMatchWholeWord.CheckedStateChanging += (s, e) => _matchWholeWord = e.Result == CheckState.Checked;
d.Add (ckbMatchWholeWord);
return d;
}
@@ -1159,14 +1159,14 @@ public class Editor : Scenario
{
X = 0, Y = Pos.Top (txtToFind) + 2, CheckedState = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase"
};
ckbMatchCase.CheckedStateChanging += (s, e) => _matchCase = e.NewValue == CheckState.Checked;
ckbMatchCase.CheckedStateChanging += (s, e) => _matchCase = e.Result == CheckState.Checked;
d.Add (ckbMatchCase);
var ckbMatchWholeWord = new CheckBox
{
X = 0, Y = Pos.Top (ckbMatchCase) + 1, CheckedState = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word"
};
ckbMatchWholeWord.CheckedStateChanging += (s, e) => _matchWholeWord = e.NewValue == CheckState.Checked;
ckbMatchWholeWord.CheckedStateChanging += (s, e) => _matchWholeWord = e.Result == CheckState.Checked;
d.Add (ckbMatchWholeWord);
return d;

View File

@@ -170,7 +170,7 @@ public class AdornmentEditor : EditorBase
_diagThicknessCheckBox.CheckedStateChanging += (s, e) =>
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
AdornmentToEdit!.Diagnostics |= ViewDiagnosticFlags.Thickness;
}
@@ -196,7 +196,7 @@ public class AdornmentEditor : EditorBase
_diagRulerCheckBox.CheckedStateChanging += (s, e) =>
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
AdornmentToEdit!.Diagnostics |= ViewDiagnosticFlags.Ruler;
}
@@ -210,7 +210,7 @@ public class AdornmentEditor : EditorBase
_diagRulerCheckBox.Y = Pos.Bottom (_diagThicknessCheckBox);
}
private EventHandler<ColorEventArgs> ColorPickerColorChanged ()
private EventHandler<ResultEventArgs<Color>> ColorPickerColorChanged ()
{
return (o, a) =>
{

View File

@@ -1,4 +1,4 @@

#nullable enable
namespace UICatalog.Scenarios;
public class AllViewsView : View
@@ -80,7 +80,7 @@ public class AllViewsView : View
if (view is { })
{
FrameView? frame = new ()
FrameView frame = new ()
{
CanFocus = true,
Title = type.Name,
@@ -181,7 +181,7 @@ public class AllViewsView : View
view.Height = MAX_VIEW_FRAME_HEIGHT - 2;
}
if (!view.Width.Has<DimAuto> (out _))
if (!view.Width!.Has<DimAuto> (out _))
{
view.Width = Dim.Fill ();
}

View File

@@ -91,9 +91,9 @@ public class BorderEditor : AdornmentEditor
SetNeedsLayout ();
}
void OnCkbTitleOnToggle (object? _, CancelEventArgs<CheckState> args)
void OnCkbTitleOnToggle (object? _, ResultEventArgs<CheckState> args)
{
if (args.NewValue == CheckState.Checked)
if (args.Result == CheckState.Checked)
{
((Border)AdornmentToEdit!).Settings |= BorderSettings.Title;
@@ -105,9 +105,9 @@ public class BorderEditor : AdornmentEditor
}
}
void OnCkbGradientOnToggle (object? _, CancelEventArgs<CheckState> args)
void OnCkbGradientOnToggle (object? _, ResultEventArgs<CheckState> args)
{
if (args.NewValue == CheckState.Checked)
if (args.Result == CheckState.Checked)
{
((Border)AdornmentToEdit!).Settings |= BorderSettings.Gradient;

View File

@@ -43,7 +43,7 @@ public class ExpanderButton : Button
Orientation = Orientation.Vertical;
HighlightStyle = HighlightStyle.None;
HighlightStates = Terminal.Gui.ViewBase.MouseState.None;
Initialized += ExpanderButton_Initialized;

View File

@@ -81,7 +81,7 @@ public class MarginEditor : AdornmentEditor
_flagSelectorTransparent.ValueChanged += (_, args) =>
{
((Margin)AdornmentToEdit!).ViewportSettings = (ViewportSettingsFlags)args.CurrentValue!;
((Margin)AdornmentToEdit!).ViewportSettings = (ViewportSettingsFlags)args.Value!;
};

View File

@@ -32,15 +32,15 @@ public class SchemeViewer : FrameView
}
/// <inheritdoc/>
protected override bool OnSettingSchemeName (in string? currentName, ref string? newName)
protected override bool OnSchemeNameChanging (ValueChangingEventArgs<string?> args)
{
Title = newName ?? "null";
Title = args.NewValue ?? "null";
foreach (VisualRoleViewer v in SubViews.OfType<VisualRoleViewer> ())
{
v.SchemeName = newName;
v.SchemeName = args.NewValue;
}
return base.OnSettingSchemeName (in currentName, ref newName);
return base.OnSchemeNameChanging (args);
}
}

View File

@@ -106,7 +106,7 @@ public class ThemeViewer : FrameView
}
}
private void OnThemeManagerOnThemeChanged (object? _, StringPropertyEventArgs args) { Title = args.NewString!; }
private void OnThemeManagerOnThemeChanged (object? _, EventArgs<string> args) { Title = args.Value!; }
protected override void Dispose (bool disposing)
{

View File

@@ -115,9 +115,9 @@ public sealed class ViewportSettingsEditor : EditorBase
Add (_cbAllowXGreaterThanContentWidth);
void AllowNegativeXToggle (object? sender, CancelEventArgs<CheckState> e)
void AllowNegativeXToggle (object? sender, ResultEventArgs<CheckState> e)
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.AllowNegativeX;
}
@@ -127,9 +127,9 @@ public sealed class ViewportSettingsEditor : EditorBase
}
}
void AllowXGreaterThanContentWidthToggle (object? sender, CancelEventArgs<CheckState> e)
void AllowXGreaterThanContentWidthToggle (object? sender, ResultEventArgs<CheckState> e)
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.AllowXGreaterThanContentWidth;
}
@@ -153,9 +153,9 @@ public sealed class ViewportSettingsEditor : EditorBase
Add (_cbAllowYGreaterThanContentHeight);
void AllowNegativeYToggle (object? sender, CancelEventArgs<CheckState> e)
void AllowNegativeYToggle (object? sender, ResultEventArgs<CheckState> e)
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.AllowNegativeY;
}
@@ -165,9 +165,9 @@ public sealed class ViewportSettingsEditor : EditorBase
}
}
void AllowYGreaterThanContentHeightToggle (object? sender, CancelEventArgs<CheckState> e)
void AllowYGreaterThanContentHeightToggle (object? sender, ResultEventArgs<CheckState> e)
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.AllowYGreaterThanContentHeight;
}
@@ -243,9 +243,9 @@ public sealed class ViewportSettingsEditor : EditorBase
};
_cbClearContentOnly.CheckedStateChanging += ClearContentOnlyToggle;
void ClearContentOnlyToggle (object? sender, CancelEventArgs<CheckState> e)
void ClearContentOnlyToggle (object? sender, ResultEventArgs<CheckState> e)
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.ClearContentOnly;
}
@@ -264,9 +264,9 @@ public sealed class ViewportSettingsEditor : EditorBase
};
_cbClipContentOnly.CheckedStateChanging += ClipContentOnlyToggle;
void ClipContentOnlyToggle (object? sender, CancelEventArgs<CheckState> e)
void ClipContentOnlyToggle (object? sender, ResultEventArgs<CheckState> e)
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.ClipContentOnly;
}
@@ -285,9 +285,9 @@ public sealed class ViewportSettingsEditor : EditorBase
};
_cbTransparent.CheckedStateChanging += TransparentToggle;
void TransparentToggle (object? sender, CancelEventArgs<CheckState> e)
void TransparentToggle (object? sender, ResultEventArgs<CheckState> e)
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.Transparent;
}
@@ -306,9 +306,9 @@ public sealed class ViewportSettingsEditor : EditorBase
};
_cbTransparentMouse.CheckedStateChanging += TransparentMouseToggle;
void TransparentMouseToggle (object? sender, CancelEventArgs<CheckState> e)
void TransparentMouseToggle (object? sender, ResultEventArgs<CheckState> e)
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
ViewToEdit!.ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.TransparentMouse;
}
@@ -327,9 +327,9 @@ public sealed class ViewportSettingsEditor : EditorBase
};
_cbVerticalScrollBar.CheckedStateChanging += VerticalScrollBarToggle;
void VerticalScrollBarToggle (object? sender, CancelEventArgs<CheckState> e)
void VerticalScrollBarToggle (object? sender, ResultEventArgs<CheckState> e)
{
ViewToEdit!.VerticalScrollBar.Visible = e.NewValue == CheckState.Checked;
ViewToEdit!.VerticalScrollBar.Visible = e.Result == CheckState.Checked;
}
_cbAutoShowVerticalScrollBar = new ()
@@ -341,9 +341,9 @@ public sealed class ViewportSettingsEditor : EditorBase
};
_cbAutoShowVerticalScrollBar.CheckedStateChanging += AutoShowVerticalScrollBarToggle;
void AutoShowVerticalScrollBarToggle (object? sender, CancelEventArgs<CheckState> e)
void AutoShowVerticalScrollBarToggle (object? sender, ResultEventArgs<CheckState> e)
{
ViewToEdit!.VerticalScrollBar.AutoShow = e.NewValue == CheckState.Checked;
ViewToEdit!.VerticalScrollBar.AutoShow = e.Result == CheckState.Checked;
}
_cbHorizontalScrollBar = new ()
@@ -355,9 +355,9 @@ public sealed class ViewportSettingsEditor : EditorBase
};
_cbHorizontalScrollBar.CheckedStateChanging += HorizontalScrollBarToggle;
void HorizontalScrollBarToggle (object? sender, CancelEventArgs<CheckState> e)
void HorizontalScrollBarToggle (object? sender, ResultEventArgs<CheckState> e)
{
ViewToEdit!.HorizontalScrollBar.Visible = e.NewValue == CheckState.Checked;
ViewToEdit!.HorizontalScrollBar.Visible = e.Result == CheckState.Checked;
}
_cbAutoShowHorizontalScrollBar = new ()
@@ -369,9 +369,9 @@ public sealed class ViewportSettingsEditor : EditorBase
};
_cbAutoShowHorizontalScrollBar.CheckedStateChanging += AutoShowHorizontalScrollBarToggle;
void AutoShowHorizontalScrollBarToggle (object? sender, CancelEventArgs<CheckState> e)
void AutoShowHorizontalScrollBarToggle (object? sender, ResultEventArgs<CheckState> e)
{
ViewToEdit!.HorizontalScrollBar.AutoShow = e.NewValue == CheckState.Checked;
ViewToEdit!.HorizontalScrollBar.AutoShow = e.Result == CheckState.Checked;
}
Add (

View File

@@ -111,8 +111,8 @@ public class Images : Scenario
_cbSupportsSixel.CheckedStateChanging += (s, e) =>
{
_sixelSupportResult.IsSupported = e.NewValue == CheckState.Checked;
SetupSixelSupported (e.NewValue == CheckState.Checked);
_sixelSupportResult.IsSupported = e.Result == CheckState.Checked;
SetupSixelSupported (e.Result == CheckState.Checked);
ApplyShowTabViewHack ();
};
@@ -126,7 +126,7 @@ public class Images : Scenario
Enabled = canTrueColor,
Text = "Use true color"
};
cbUseTrueColor.CheckedStateChanging += (_, evt) => Application.Force16Colors = evt.NewValue == CheckState.UnChecked;
cbUseTrueColor.CheckedStateChanging += (_, evt) => Application.Force16Colors = evt.Result == CheckState.UnChecked;
_win.Add (cbUseTrueColor);
var btnOpenImage = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 0, Text = "Open Image" };

View File

@@ -123,9 +123,9 @@ public class ListViewWithSelection : Scenario
Application.Shutdown ();
}
private void CustomRenderCB_Toggle (object sender, CancelEventArgs<CheckState> stateEventArgs)
private void CustomRenderCB_Toggle (object sender, ResultEventArgs<CheckState> stateEventArgs)
{
if (stateEventArgs.CurrentValue == CheckState.Checked)
if (stateEventArgs.Result == CheckState.Checked)
{
_listView.SetSource (_scenarios);
}
@@ -137,23 +137,23 @@ public class ListViewWithSelection : Scenario
_appWindow.SetNeedsDraw ();
}
private void AllowsMarkingCB_Toggle (object sender, [NotNull] CancelEventArgs<CheckState> stateEventArgs)
private void AllowsMarkingCB_Toggle (object sender, [NotNull] ResultEventArgs<CheckState> stateEventArgs)
{
_listView.AllowsMarking = stateEventArgs.NewValue == CheckState.Checked;
_listView.AllowsMarking = stateEventArgs.Result == CheckState.Checked;
_allowMultipleCb.Enabled = _listView.AllowsMarking;
_appWindow.SetNeedsDraw ();
}
private void AllowsMultipleSelectionCB_Toggle (object sender, [NotNull] CancelEventArgs<CheckState> stateEventArgs)
private void AllowsMultipleSelectionCB_Toggle (object sender, [NotNull] ResultEventArgs<CheckState> stateEventArgs)
{
_listView.AllowsMultipleSelection = stateEventArgs.NewValue == CheckState.Checked;
_listView.AllowsMultipleSelection = stateEventArgs.Result == CheckState.Checked;
_appWindow.SetNeedsDraw ();
}
private void AllowYGreaterThanContentHeightCB_Toggle (object sender, [NotNull] CancelEventArgs<CheckState> stateEventArgs)
private void AllowYGreaterThanContentHeightCB_Toggle (object sender, [NotNull] ResultEventArgs<CheckState> stateEventArgs)
{
if (stateEventArgs.NewValue == CheckState.Checked)
if (stateEventArgs.Result == CheckState.Checked)
{
_listView.ViewportSettings |= Terminal.Gui.ViewBase.ViewportSettingsFlags.AllowYGreaterThanContentHeight;
}

View File

@@ -1,11 +1,8 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Collections.ObjectModel;
namespace UICatalog.Scenarios;
[ScenarioMetadata ("Mouse", "Demonstrates how to capture mouse events")]
[ScenarioMetadata ("Mouse", "Demonstrates Mouse Events and States")]
[ScenarioCategory ("Mouse and Keyboard")]
public class Mouse : Scenario
{
@@ -15,6 +12,7 @@ public class Mouse : Scenario
Window win = new ()
{
Id = "win",
Title = GetQuitKeyAndName ()
};
@@ -73,104 +71,151 @@ public class Mouse : Scenario
win.Add (cbWantContinuousPresses);
CheckBox cbHighlightOnPress = new ()
CheckBox cbHighlightOnPressed = new ()
{
X = Pos.Right (filterSlider),
Y = Pos.Bottom (cbWantContinuousPresses),
Title = "_Highlight on Press"
Title = "_Highlight on Pressed"
};
win.Add (cbHighlightOnPress);
win.Add (cbHighlightOnPressed);
CheckBox cbHighlightOnPressedOutside = new ()
{
X = Pos.Right (filterSlider),
Y = Pos.Bottom (cbHighlightOnPressed),
Title = "_Highlight on PressedOutside"
};
win.Add (cbHighlightOnPressedOutside);
var demo = new MouseEventDemoView
{
Id = "demo",
X = Pos.Right (filterSlider),
Y = Pos.Bottom (cbHighlightOnPress),
Y = Pos.Bottom (cbHighlightOnPressedOutside),
Width = Dim.Fill (),
Height = 15,
Title = "Enter/Leave Demo",
Title = "Enter/Leave Demo"
};
demo.Padding.Initialized += DemoPaddingOnInitialized;
demo.Padding!.Initialized += DemoPaddingOnInitialized;
void DemoPaddingOnInitialized (object o, EventArgs eventArgs)
{
demo.Padding.Add (
new MouseEventDemoView ()
{
X = 0,
Y = 0,
Width = Dim.Fill (),
Height = Dim.Func (() => demo.Padding.Thickness.Top),
Title = "inPadding",
Id = "inPadding"
});
demo.Padding!.Add (
new MouseEventDemoView
{
X = 0,
Y = 0,
Width = Dim.Fill (),
Height = Dim.Func (() => demo.Padding.Thickness.Top),
Title = "inPadding",
Id = "inPadding"
});
demo.Padding.Thickness = demo.Padding.Thickness with { Top = 5 };
}
View sub1 = new MouseEventDemoView ()
View sub1 = new MouseEventDemoView
{
X = 0,
Y = 0,
Width = Dim.Percent (20),
Height = Dim.Fill (),
Title = "sub1",
Id = "sub1",
Id = "sub1"
};
demo.Add (sub1);
demo.Add (
new MouseEventDemoView ()
{
X = Pos.Right (sub1) - 4,
Y = Pos.Top (sub1) + 1,
Width = Dim.Percent (20),
Height = Dim.Fill (1),
Title = "sub2",
Id = "sub2",
});
View sub2 = new MouseEventDemoView
{
X = Pos.Right (sub1) - 4,
Y = Pos.Top (sub1) + 1,
Width = Dim.Percent (20),
Height = Dim.Fill (1),
Title = "sub2",
Id = "sub2"
};
demo.Add (sub2);
win.Add (demo);
cbHighlightOnPress.CheckedState = demo.HighlightStyle == (HighlightStyle.Pressed | HighlightStyle.PressedOutside) ? CheckState.Checked : CheckState.UnChecked;
cbHighlightOnPressed.CheckedState = demo.HighlightStates.HasFlag (MouseState.Pressed) ? CheckState.Checked : CheckState.UnChecked;
// BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/3753
cbHighlightOnPress.CheckedStateChanging += (s, e) =>
{
if (e.NewValue == CheckState.Checked)
{
demo.HighlightStyle = HighlightStyle.Pressed | HighlightStyle.PressedOutside;
}
else
{
demo.HighlightStyle = HighlightStyle.None;
}
cbHighlightOnPressed.CheckedStateChanging += (s, e) =>
{
if (e.Result == CheckState.Checked)
{
demo.HighlightStates |= MouseState.Pressed;
}
else
{
demo.HighlightStates &= ~MouseState.Pressed;
}
foreach (View subview in demo.SubViews)
{
if (e.NewValue == CheckState.Checked)
{
subview.HighlightStyle = HighlightStyle.Pressed | HighlightStyle.PressedOutside;
}
else
{
subview.HighlightStyle = HighlightStyle.None;
}
}
foreach (View subview in demo.SubViews)
{
if (e.Result == CheckState.Checked)
{
subview.HighlightStates |= MouseState.Pressed;
}
else
{
subview.HighlightStates &= ~MouseState.Pressed;
}
}
foreach (View subview in demo.Padding.SubViews)
{
if (e.NewValue == CheckState.Checked)
{
subview.HighlightStyle = HighlightStyle.Pressed | HighlightStyle.PressedOutside;
}
else
{
subview.HighlightStyle = HighlightStyle.None;
}
}
foreach (View subview in demo.Padding.SubViews)
{
if (e.Result == CheckState.Checked)
{
subview.HighlightStates |= MouseState.Pressed;
}
else
{
subview.HighlightStates &= ~MouseState.Pressed;
}
}
};
};
cbHighlightOnPressedOutside.CheckedState = demo.HighlightStates.HasFlag (MouseState.PressedOutside) ? CheckState.Checked : CheckState.UnChecked;
cbHighlightOnPressedOutside.CheckedStateChanging += (s, e) =>
{
if (e.Result == CheckState.Checked)
{
demo.HighlightStates |= MouseState.PressedOutside;
}
else
{
demo.HighlightStates &= ~MouseState.PressedOutside;
}
foreach (View subview in demo.SubViews)
{
if (e.Result == CheckState.Checked)
{
subview.HighlightStates |= MouseState.PressedOutside;
}
else
{
subview.HighlightStates &= ~MouseState.PressedOutside;
}
}
foreach (View subview in demo.Padding.SubViews)
{
if (e.Result == CheckState.Checked)
{
subview.HighlightStates |= MouseState.PressedOutside;
}
else
{
subview.HighlightStates &= ~MouseState.PressedOutside;
}
}
};
cbWantContinuousPresses.CheckedStateChanging += (s, e) =>
{
@@ -185,10 +230,8 @@ public class Mouse : Scenario
{
subview.WantContinuousButtonPressed = demo.WantContinuousButtonPressed;
}
};
var label = new Label
{
Text = "_App Events:",
@@ -241,12 +284,12 @@ public class Mouse : Scenario
win.Add (label, winLog);
clearButton.Accepting += (s, e) =>
{
appLogList.Clear ();
appLog.SetSource (appLogList);
winLogList.Clear ();
winLog.SetSource (winLogList);
};
{
appLogList.Clear ();
appLog.SetSource (appLogList);
winLogList.Clear ();
winLog.SetSource (winLogList);
};
win.MouseEvent += (sender, a) =>
{
@@ -277,42 +320,56 @@ public class Mouse : Scenario
CanFocus = true;
Id = "mouseEventDemoView";
Padding!.Thickness = new Thickness (1, 1, 1, 1);
Initialized += OnInitialized;
MouseLeave += (s, e) => { Text = "Leave"; };
MouseEnter += (s, e) => { Text = "Enter"; };
return;
void OnInitialized (object sender, EventArgs e)
{
TextAlignment = Alignment.Center;
VerticalTextAlignment = Alignment.Center;
Padding!.SetScheme (new Scheme (new Attribute (Color.Black)));
Padding!.Thickness = new (1, 1, 1, 1);
Padding!.SetScheme (new (new Attribute (Color.Black)));
Padding.Id = $"{Id}.Padding";
Padding.MouseEnter += PaddingOnMouseEnter;
Padding.MouseLeave += PaddingOnMouseLeave;
void PaddingOnMouseEnter (object o, CancelEventArgs e)
{
Padding.SchemeName = "Error";
}
void PaddingOnMouseLeave (object o, EventArgs e)
{
Padding.SchemeName = "Dialog";
}
Border!.Thickness = new Thickness (1);
Border!.Thickness = new (1);
Border.LineStyle = LineStyle.Rounded;
Border.Id = $"{Id}.Border";
MouseStateChanged += (_, args) =>
{
if (args.Value.HasFlag (MouseState.PressedOutside))
{
Border.LineStyle = LineStyle.Dotted;
}
else
{
Border.LineStyle = LineStyle.Single;
}
SetNeedsDraw ();
};
}
}
/// <inheritdoc/>
protected override bool OnGettingAttributeForRole (in VisualRole role, ref Attribute currentAttribute)
{
if (role == VisualRole.Normal)
{
if (MouseState.HasFlag (MouseState.Pressed) && HighlightStates.HasFlag (MouseState.Pressed))
{
currentAttribute = currentAttribute with { Background = currentAttribute.Foreground.GetBrighterColor () };
return true;
}
}
MouseLeave += (s, e) =>
{
Text = "Leave";
};
MouseEnter += (s, e) =>
{
Text = "Enter";
};
return base.OnGettingAttributeForRole (in role, ref currentAttribute);
}
}
}

View File

@@ -220,9 +220,9 @@ public class Navigation : Scenario
return;
void ColorPicker_ColorChanged (object sender, ColorEventArgs e)
void ColorPicker_ColorChanged (object sender, ResultEventArgs<Color> e)
{
testFrame.SetScheme (testFrame.GetScheme () with { Normal = new (testFrame.GetAttributeForRole (VisualRole.Normal).Foreground, e.CurrentValue) });
testFrame.SetScheme (testFrame.GetScheme () with { Normal = new (testFrame.GetAttributeForRole (VisualRole.Normal).Foreground, e.Result) });
}
}

View File

@@ -101,14 +101,14 @@ public sealed class PosAlignDemo : Scenario
{
if (dimension == Dimension.Width)
{
_horizAligner.AlignmentModes = e.NewValue == CheckState.Checked
_horizAligner.AlignmentModes = e.Result == CheckState.Checked
? _horizAligner.AlignmentModes | AlignmentModes.EndToStart
: _horizAligner.AlignmentModes & ~AlignmentModes.EndToStart;
UpdatePosAlignObjects (appWindow, dimension, _horizAligner);
}
else
{
_vertAligner.AlignmentModes = e.NewValue == CheckState.Checked
_vertAligner.AlignmentModes = e.Result == CheckState.Checked
? _vertAligner.AlignmentModes | AlignmentModes.EndToStart
: _vertAligner.AlignmentModes & ~AlignmentModes.EndToStart;
UpdatePosAlignObjects (appWindow, dimension, _vertAligner);
@@ -139,14 +139,14 @@ public sealed class PosAlignDemo : Scenario
{
if (dimension == Dimension.Width)
{
_horizAligner.AlignmentModes = e.NewValue == CheckState.Checked
_horizAligner.AlignmentModes = e.Result == CheckState.Checked
? _horizAligner.AlignmentModes | AlignmentModes.IgnoreFirstOrLast
: _horizAligner.AlignmentModes & ~AlignmentModes.IgnoreFirstOrLast;
UpdatePosAlignObjects (appWindow, dimension, _horizAligner);
}
else
{
_vertAligner.AlignmentModes = e.NewValue == CheckState.Checked
_vertAligner.AlignmentModes = e.Result == CheckState.Checked
? _vertAligner.AlignmentModes | AlignmentModes.IgnoreFirstOrLast
: _vertAligner.AlignmentModes & ~AlignmentModes.IgnoreFirstOrLast;
UpdatePosAlignObjects (appWindow, dimension, _vertAligner);
@@ -177,14 +177,14 @@ public sealed class PosAlignDemo : Scenario
{
if (dimension == Dimension.Width)
{
_horizAligner.AlignmentModes = e.NewValue == CheckState.Checked
_horizAligner.AlignmentModes = e.Result == CheckState.Checked
? _horizAligner.AlignmentModes | AlignmentModes.AddSpaceBetweenItems
: _horizAligner.AlignmentModes & ~AlignmentModes.AddSpaceBetweenItems;
UpdatePosAlignObjects (appWindow, dimension, _horizAligner);
}
else
{
_vertAligner.AlignmentModes = e.NewValue == CheckState.Checked
_vertAligner.AlignmentModes = e.Result == CheckState.Checked
? _vertAligner.AlignmentModes | AlignmentModes.AddSpaceBetweenItems
: _vertAligner.AlignmentModes & ~AlignmentModes.AddSpaceBetweenItems;
UpdatePosAlignObjects (appWindow, dimension, _vertAligner);
@@ -214,12 +214,12 @@ public sealed class PosAlignDemo : Scenario
{
if (dimension == Dimension.Width)
{
_leftMargin = e.NewValue == CheckState.Checked ? 1 : 0;
_leftMargin = e.Result == CheckState.Checked ? 1 : 0;
UpdatePosAlignObjects (appWindow, dimension, _horizAligner);
}
else
{
_topMargin = e.NewValue == CheckState.Checked ? 1 : 0;
_topMargin = e.Result == CheckState.Checked ? 1 : 0;
UpdatePosAlignObjects (appWindow, dimension, _vertAligner);
}
};

View File

@@ -268,7 +268,7 @@ public class ProgressBarStyles : Scenario
ckbBidirectional.CheckedStateChanging += (s, e) =>
{
marqueesBlocksPB.BidirectionalMarquee =
marqueesContinuousPB.BidirectionalMarquee = e.NewValue == CheckState.Checked;
marqueesContinuousPB.BidirectionalMarquee = e.Result == CheckState.Checked;
};

View File

@@ -274,7 +274,7 @@ public class ScrollBarDemo : Scenario
Text = $"_AutoShow",
CheckedState = scrollBar.AutoShow ? CheckState.Checked : CheckState.UnChecked
};
autoShow.CheckedStateChanging += (s, e) => scrollBar.AutoShow = e.NewValue == CheckState.Checked;
autoShow.CheckedStateChanging += (s, e) => scrollBar.AutoShow = e.Result == CheckState.Checked;
demoFrame.Add (autoShow);
var lblSliderPosition = new Label
@@ -352,33 +352,33 @@ public class ScrollBarDemo : Scenario
{
scrollBar.ScrollableContentSizeChanged += (s, e) =>
{
eventLog.Log ($"SizeChanged: {e.CurrentValue}");
eventLog.Log ($"SizeChanged: {e.Value}");
if (scrollContentSize.Value != e.CurrentValue)
if (scrollContentSize.Value != e.Value)
{
scrollContentSize.Value = e.CurrentValue;
scrollContentSize.Value = e.Value;
}
};
scrollBar.SliderPositionChanged += (s, e) =>
{
eventLog.Log ($"SliderPositionChanged: {e.CurrentValue}");
eventLog.Log ($"SliderPositionChanged: {e.Value}");
eventLog.Log ($" Position: {scrollBar.Position}");
scrollSliderPosition.Text = e.CurrentValue.ToString ();
scrollSliderPosition.Text = e.Value.ToString ();
};
scrollBar.Scrolled += (s, e) =>
{
eventLog.Log ($"Scrolled: {e.CurrentValue}");
eventLog.Log ($"Scrolled: {e.Value}");
eventLog.Log ($" SliderPosition: {scrollBar.GetSliderPosition ()}");
scrolled.Text = e.CurrentValue.ToString ();
scrolled.Text = e.Value.ToString ();
};
scrollBar.PositionChanged += (s, e) =>
{
eventLog.Log ($"PositionChanged: {e.CurrentValue}");
scrollPosition.Value = e.CurrentValue;
controlledList.Viewport = controlledList.Viewport with { Y = e.CurrentValue };
eventLog.Log ($"PositionChanged: {e.Value}");
scrollPosition.Value = e.Value;
controlledList.Viewport = controlledList.Viewport with { Y = e.Value };
};

View File

@@ -53,7 +53,7 @@ public class Scrolling : Scenario
CheckedState = demoView.HorizontalScrollBar.Visible ? CheckState.Checked : CheckState.UnChecked
};
app.Add (hCheckBox);
hCheckBox.CheckedStateChanged += (sender, args) => { demoView.HorizontalScrollBar.Visible = args.CurrentValue == CheckState.Checked; };
hCheckBox.CheckedStateChanged += (sender, args) => { demoView.HorizontalScrollBar.Visible = args.Value == CheckState.Checked; };
//// NOTE: This call to EnableScrollBar is technically not needed because the reference
//// NOTE: to demoView.HorizontalScrollBar below will cause it to be lazy created.
@@ -67,7 +67,7 @@ public class Scrolling : Scenario
CheckedState = demoView.VerticalScrollBar.Visible ? CheckState.Checked : CheckState.UnChecked
};
app.Add (vCheckBox);
vCheckBox.CheckedStateChanged += (sender, args) => { demoView.VerticalScrollBar.Visible = args.CurrentValue == CheckState.Checked; };
vCheckBox.CheckedStateChanged += (sender, args) => { demoView.VerticalScrollBar.Visible = args.Value == CheckState.Checked; };
var ahCheckBox = new CheckBox
{
@@ -79,8 +79,8 @@ public class Scrolling : Scenario
ahCheckBox.CheckedStateChanging += (s, e) =>
{
demoView.HorizontalScrollBar.AutoShow = e.NewValue == CheckState.Checked;
demoView.VerticalScrollBar.AutoShow = e.NewValue == CheckState.Checked;
demoView.HorizontalScrollBar.AutoShow = e.Result == CheckState.Checked;
demoView.VerticalScrollBar.AutoShow = e.Result == CheckState.Checked;
};
app.Add (ahCheckBox);

View File

@@ -82,7 +82,7 @@ public class ShadowStyles : Scenario
colorPicker.ColorChanged += (sender, args) =>
{
var normal = app.GetScheme ().Normal;
app.SetScheme (app.GetScheme() with {Normal = new Attribute(normal.Foreground, args.CurrentValue)});
app.SetScheme (app.GetScheme() with {Normal = new Attribute(normal.Foreground, args.Result)});
};
app.Add (button, colorPicker);

View File

@@ -64,7 +64,7 @@ public class Shortcuts : Scenario
{
Text = "_Align Keys",
CanFocus = false,
HighlightStyle = HighlightStyle.None
HighlightStates = MouseState.None
},
Key = Key.F5.WithCtrl.WithAlt.WithShift
};
@@ -85,7 +85,7 @@ public class Shortcuts : Scenario
v => v is Shortcut { Width: not DimAbsolute });
IEnumerable<View> enumerable = toAlign as View [] ?? toAlign.ToArray ();
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
max = (from Shortcut? peer in enumerable
select peer.Key.ToString ().GetColumns ()).Prepend (max)
@@ -118,7 +118,7 @@ public class Shortcuts : Scenario
{
Text = "Command _First",
CanFocus = false,
HighlightStyle = HighlightStyle.None
HighlightStates = MouseState.None
},
Key = Key.F.WithCtrl
};
@@ -143,7 +143,7 @@ public class Shortcuts : Scenario
{
var peer = (Shortcut)view;
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
peer.AlignmentModes &= ~AlignmentModes.EndToStart;
}
@@ -181,7 +181,7 @@ public class Shortcuts : Scenario
{
if (peer.CanFocus)
{
peer.CommandView.CanFocus = e.NewValue == CheckState.Checked;
peer.CommandView.CanFocus = e.Result == CheckState.Checked;
}
}
}
@@ -213,7 +213,7 @@ public class Shortcuts : Scenario
{
Title = "_Button",
ShadowStyle = ShadowStyle.None,
HighlightStyle = HighlightStyle.None
HighlightStates = MouseState.None
},
Key = Key.K
};
@@ -449,7 +449,7 @@ public class Shortcuts : Scenario
{
if (o is { })
{
eventSource.Add ($"ColorChanged: {o.GetType ().Name} - {args.CurrentValue}");
eventSource.Add ($"ColorChanged: {o.GetType ().Name} - {args.Result}");
eventLog.MoveDown ();
Application.Top.SetScheme (
@@ -457,7 +457,7 @@ public class Shortcuts : Scenario
{
Normal = new (
Application.Top!.GetAttributeForRole (VisualRole.Normal).Foreground,
args.CurrentValue,
args.Result,
Application.Top!.GetAttributeForRole (VisualRole.Normal).Style)
});
}

View File

@@ -162,9 +162,9 @@ public class SpinnerViewStyles : Scenario
}
};
ckbReverse.CheckedStateChanging += (s, e) => { spinner.SpinReverse = e.NewValue == CheckState.Checked; };
ckbReverse.CheckedStateChanging += (s, e) => { spinner.SpinReverse = e.Result == CheckState.Checked; };
ckbBounce.CheckedStateChanging += (s, e) => { spinner.SpinBounce = e.NewValue == CheckState.Checked; };
ckbBounce.CheckedStateChanging += (s, e) => { spinner.SpinBounce = e.Result == CheckState.Checked; };
app.Unloaded += App_Unloaded;

View File

@@ -501,7 +501,7 @@ public class TextAlignmentAndDirection : Scenario
Enabled = false
};
justifyCheckbox.CheckedStateChanging += (s, e) => ToggleJustify (e.NewValue != CheckState.Checked);
justifyCheckbox.CheckedStateChanging += (s, e) => ToggleJustify (e.Result != CheckState.Checked);
justifyOptions.SelectedItemChanged += (s, e) => { ToggleJustify (false, true); };
@@ -521,7 +521,7 @@ public class TextAlignmentAndDirection : Scenario
wrapCheckbox.CheckedStateChanging += (s, e) =>
{
if (e.CurrentValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
foreach (View t in multiLineLabels)
{

View File

@@ -56,7 +56,7 @@ public class TextEffectsScenario : Scenario
cbLooping.CheckedStateChanging += (s, e) =>
{
_loopingGradient = e.NewValue == CheckState.Checked;
_loopingGradient = e.Result == CheckState.Checked;
SetupGradientLineCanvas (w, w.Frame.Size);
};

View File

@@ -124,8 +124,8 @@ public class TextFormatterDemo : Scenario
{
for (int i = 0; i < alignments.Count; i++)
{
singleLines [i].Text = e.CurrentValue == CheckState.Checked ? text : unicode;
multipleLines [i].Text = e.CurrentValue == CheckState.Checked ? text : unicode;
singleLines [i].Text = e.Result == CheckState.Checked ? text : unicode;
multipleLines [i].Text = e.Result == CheckState.Checked ? text : unicode;
}
};

View File

@@ -33,9 +33,9 @@ public class TextInputControls : Scenario
textField.Autocomplete.SuggestionGenerator = singleWordGenerator;
textField.TextChanging += TextFieldTextChanging;
void TextFieldTextChanging (object sender, CancelEventArgs<string> e)
void TextFieldTextChanging (object sender, ResultEventArgs<string> e)
{
singleWordGenerator.AllSuggestions = Regex.Matches (e.NewValue, "\\w+")
singleWordGenerator.AllSuggestions = Regex.Matches (e.Result, "\\w+")
.Select (s => s.Value)
.Distinct ()
.ToList ();
@@ -121,7 +121,7 @@ public class TextInputControls : Scenario
{
X = Pos.Left (textView), Y = Pos.Bottom (textView), CheckedState = textView.ReadOnly ? CheckState.Checked : CheckState.UnChecked, Text = "Read_Only"
};
chxReadOnly.CheckedStateChanging += (sender, args) => textView.ReadOnly = args.NewValue == CheckState.Checked;
chxReadOnly.CheckedStateChanging += (sender, args) => textView.ReadOnly = args.Result == CheckState.Checked;
win.Add (chxReadOnly);
// By default TextView is a multi-line control. It can be forced to
@@ -139,7 +139,7 @@ public class TextInputControls : Scenario
CheckedState = textView.WordWrap ? CheckState.Checked : CheckState.UnChecked,
Text = "_Word Wrap"
};
chxWordWrap.CheckedStateChanging += (s, e) => textView.WordWrap = e.NewValue == CheckState.Checked;
chxWordWrap.CheckedStateChanging += (s, e) => textView.WordWrap = e.Result == CheckState.Checked;
win.Add (chxWordWrap);
// TextView captures Tabs (so users can enter /t into text) by default;
@@ -155,7 +155,7 @@ public class TextInputControls : Scenario
chxMultiline.CheckedStateChanging += (s, e) =>
{
textView.Multiline = e.NewValue == CheckState.Checked;
textView.Multiline = e.Result == CheckState.Checked;
if (!textView.Multiline && chxWordWrap.CheckedState == CheckState.Checked)
{
@@ -173,7 +173,7 @@ public class TextInputControls : Scenario
chxCaptureTabs.CheckedStateChanging += (s, e) =>
{
if (e.NewValue == CheckState.Checked)
if (e.Result == CheckState.Checked)
{
textView.KeyBindings.Add (keyTab, Command.Tab);
textView.KeyBindings.Add (keyBackTab, Command.BackTab);
@@ -184,7 +184,7 @@ public class TextInputControls : Scenario
textView.KeyBindings.Remove (keyBackTab);
}
textView.AllowsTab = e.NewValue == CheckState.Checked;
textView.AllowsTab = e.Result == CheckState.Checked;
};
win.Add (chxCaptureTabs);

View File

@@ -81,8 +81,13 @@ public sealed class TestStyles : Scenario
{
return;
}
args.NewValue = args.NewValue with { Style = style };
args.Cancel = true;
if (args.Result is { })
{
args.Result = args.Result.Value with { Style = style };
}
args.Handled = true;
};
appWindow.Add (button);
@@ -125,8 +130,12 @@ public sealed class TestStyles : Scenario
};
button.GettingAttributeForRole += (_, args) =>
{
args.NewValue = args.NewValue with { Style = combination };
args.Cancel = true;
if (args.Result is { })
{
args.Result = args.Result.Value with { Style = combination };
}
args.Handled = true;
};
appWindow.Add (button);
}

View File

@@ -125,26 +125,26 @@ public sealed class Themes : Scenario
viewListView.SelectedItem = 0;
themeViewer.SettingSchemeName += (sender, args) =>
{
if (_view is { })
{
Application.Top!.SchemeName = args.NewString;
themeViewer.SchemeNameChanging += (sender, args) =>
{
if (_view is { })
{
Application.Top!.SchemeName = args.NewValue;
if (_view.HasScheme)
{
_view.SetScheme (null);
}
if (_view.HasScheme)
{
_view.SetScheme (null);
}
_view.SchemeName = args.NewString;
}
};
_view.SchemeName = args.NewValue;
}
};
AllViewsView? allViewsView = null;
allViewsCheckBox.CheckedStateChanged += (sender, args) =>
{
if (args.CurrentValue == CheckState.Checked)
if (args.Value == CheckState.Checked)
{
viewListView.Visible = false;
appWindow.Remove (viewFrame);

View File

@@ -46,7 +46,7 @@ public class TrueColors : Scenario
Enabled = canTrueColor,
Text = "Force 16 colors"
};
cbUseTrueColor.CheckedStateChanging += (_, evt) => { Application.Force16Colors = evt.NewValue == CheckState.Checked; };
cbUseTrueColor.CheckedStateChanging += (_, evt) => { Application.Force16Colors = evt.Result == CheckState.Checked; };
app.Add (cbUseTrueColor);
y += 2;

View File

@@ -143,7 +143,7 @@ public class ViewportSettings : Scenario
{
Normal = new (
colorPicker.SuperView.GetAttributeForRole (VisualRole.Normal).Foreground,
e.CurrentValue
e.Result
)
});
};

View File

@@ -406,7 +406,7 @@ public class UICatalog
void ApplicationOnInitializedChanged (object? sender, EventArgs<bool> e)
{
if (e.CurrentValue)
if (e.Value)
{
sw.Start ();
}

View File

@@ -168,9 +168,9 @@ public class UICatalogTop : Toplevel
_force16ColorsMenuItemCb.CheckedStateChanged += (sender, args) =>
{
Application.Force16Colors = args.CurrentValue == CheckState.Checked;
Application.Force16Colors = args.Value == CheckState.Checked;
_force16ColorsShortcutCb!.CheckedState = args.CurrentValue;
_force16ColorsShortcutCb!.CheckedState = args.Value;
Application.LayoutAndDraw ();
};
@@ -186,7 +186,7 @@ public class UICatalogTop : Toplevel
{
_themesRg = new ()
{
HighlightStyle = HighlightStyle.None,
HighlightStates = Terminal.Gui.ViewBase.MouseState.None,
};
_themesRg.SelectedItemChanged += (_, args) =>
@@ -210,7 +210,7 @@ public class UICatalogTop : Toplevel
_topSchemeRg = new ()
{
HighlightStyle = HighlightStyle.None,
HighlightStates = Terminal.Gui.ViewBase.MouseState.None,
};
_topSchemeRg.SelectedItemChanged += (_, args) =>
@@ -261,7 +261,7 @@ public class UICatalogTop : Toplevel
{
CanFocus = true,
Styles = FlagSelectorStyles.ShowNone,
HighlightStyle = HighlightStyle.None,
HighlightStates = Terminal.Gui.ViewBase.MouseState.None,
};
_diagnosticFlagsSelector.UsedHotKeys.Add (Key.D);
_diagnosticFlagsSelector.AssignHotKeysToCheckBoxes = true;
@@ -287,7 +287,7 @@ public class UICatalogTop : Toplevel
CheckedState = Application.IsMouseDisabled ? CheckState.Checked : CheckState.UnChecked
};
_disableMouseCb.CheckedStateChanged += (_, args) => { Application.IsMouseDisabled = args.CurrentValue == CheckState.Checked; };
_disableMouseCb.CheckedStateChanged += (_, args) => { Application.IsMouseDisabled = args.Value == CheckState.Checked; };
menuItems.Add (
new MenuItemv2
@@ -310,7 +310,7 @@ public class UICatalogTop : Toplevel
AssignHotKeysToCheckBoxes = true,
Options = Enum.GetNames<LogLevel> (),
SelectedItem = logLevels.ToList ().IndexOf (Enum.Parse<LogLevel> (UICatalog.Options.DebugLogLevel)),
HighlightStyle = HighlightStyle.Hover
HighlightStates = Terminal.Gui.ViewBase.MouseState.In
};
_logLevelRg.SelectedItemChanged += (_, args) =>
@@ -634,8 +634,8 @@ public class UICatalogTop : Toplevel
_force16ColorsShortcutCb.CheckedStateChanging += (sender, args) =>
{
Application.Force16Colors = args.NewValue == CheckState.Checked;
_force16ColorsMenuItemCb!.CheckedState = args.NewValue;
Application.Force16Colors = args.Result == CheckState.Checked;
_force16ColorsMenuItemCb!.CheckedState = args.Result;
Application.LayoutAndDraw ();
};

View File

@@ -0,0 +1,56 @@
#nullable enable
namespace Terminal.Gui.App;
using System;
/// <summary>
/// Provides helper methods for executing event-driven workflows in the Cancellable Work Pattern (CWP).
/// </summary>
/// <remarks>
/// <para>
/// Used for workflows where an event is raised to allow cancellation or customization of a result,
/// such as in <see cref="Application.RaiseKeyDownEvent"/>. The <see cref="Execute{T}"/> method invokes an
/// event handler and returns whether the operation was handled, supporting result production
/// scenarios with <see cref="ResultEventArgs{T}"/>.
/// </para>
/// </remarks>
/// <seealso cref="ResultEventArgs{T}"/>
public static class CWPEventHelper
{
/// <summary>
/// Executes an event-driven CWP workflow by raising an event.
/// </summary>
/// <typeparam name="T">The type of the result in the event arguments.</typeparam>
/// <param name="eventHandler">The event handler to invoke, or null if no handler is subscribed.</param>
/// <param name="args">The event arguments, containing a result and handled status.</param>
/// <returns>True if the event was handled, false otherwise.</returns>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="args"/> is null.</exception>
/// <example>
/// <code>
/// EventHandler&lt;ResultEventArgs&lt;Key&gt;&gt;? keyDownHandler = (sender, args) =>
/// {
/// if (args.Result?.KeyCode == KeyCode.Q | KeyCode.CtrlMask)
/// {
/// args.Handled = true;
/// }
/// };
/// ResultEventArgs&lt;Key&gt; args = new(new Key(KeyCode.Q | KeyCode.CtrlMask));
/// bool handled = CWPEventHelper.Execute(keyDownHandler, args);
/// </code>
/// </example>
public static bool Execute<T> (
EventHandler<ResultEventArgs<T>>? eventHandler,
ResultEventArgs<T> args)
{
ArgumentNullException.ThrowIfNull (args);
if (eventHandler == null)
{
return false;
}
eventHandler.Invoke (null, args);
return args.Handled;
}
}

View File

@@ -0,0 +1,102 @@
namespace Terminal.Gui.App;
#nullable enable
/// <summary>
/// Provides helper methods for executing property change workflows in the Cancellable Work Pattern (CWP).
/// </summary>
/// <remarks>
/// <para>
/// Used for workflows where a property value is modified, such as in <see cref="OrientationHelper"/> or
/// <see cref="View.SchemeName"/>, allowing pre- and post-change events to customize or cancel the change.
/// </para>
/// </remarks>
/// <seealso cref="ValueChangingEventArgs{T}"/>
/// <seealso cref="ValueChangedEventArgs{T}"/>
public static class CWPPropertyHelper
{
/// <summary>
/// Executes a CWP workflow for a property change, with pre- and post-change events.
/// </summary>
/// <typeparam name="T">
/// The type of the property value, which may be a nullable reference type (e.g., <see cref="string"/>
/// ?).
/// </typeparam>
/// <param name="currentValue">The current property value, which may be null for nullable types.</param>
/// <param name="newValue">The proposed new property value, which may be null for nullable types.</param>
/// <param name="onChanging">The virtual method invoked before the change, returning true to cancel.</param>
/// <param name="changingEvent">The pre-change event raised to allow modification or cancellation.</param>
/// <param name="onChanged">The virtual method invoked after the change.</param>
/// <param name="changedEvent">The post-change event raised to notify of the completed change.</param>
/// <param name="finalValue">
/// The final value after the workflow, reflecting any modifications, which may be null for
/// nullable types.
/// </param>
/// <returns>True if the property was changed, false if cancelled.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if <see cref="ValueChangingEventArgs{T}.NewValue"/> is null for non-nullable reference types after the
/// workflow.
/// </exception>
/// <example>
/// <code>
/// string? current = null;
/// string? proposed = "Base";
/// Func&lt;ValueChangingEventArgs&lt;string?&gt;, bool&gt; onChanging = args =&gt; false;
/// EventHandler&lt;ValueChangingEventArgs&lt;string?&gt;&gt;? changingEvent = null;
/// Action&lt;ValueChangedEventArgs&lt;string?&gt;&gt;? onChanged = args =&gt;
/// Console.WriteLine($"SchemeName changed to {args.NewValue ?? "none"}.");
/// EventHandler&lt;ValueChangedEventArgs&lt;string?&gt;&gt;? changedEvent = null;
/// bool changed = CWPPropertyHelper.ChangeProperty(
/// current, proposed, onChanging, changingEvent, onChanged, changedEvent, out string? final);
/// </code>
/// </example>
public static bool ChangeProperty<T> (
T currentValue,
T newValue,
Func<ValueChangingEventArgs<T>, bool> onChanging,
EventHandler<ValueChangingEventArgs<T>>? changingEvent,
Action<ValueChangedEventArgs<T>>? onChanged,
EventHandler<ValueChangedEventArgs<T>>? changedEvent,
out T finalValue
)
{
if (EqualityComparer<T>.Default.Equals (currentValue, newValue))
{
finalValue = currentValue;
return false;
}
ValueChangingEventArgs<T> args = new (currentValue, newValue);
bool cancelled = onChanging (args) || args.Handled;
if (cancelled)
{
finalValue = currentValue;
return false;
}
changingEvent?.Invoke (null, args);
if (args.Handled)
{
finalValue = currentValue;
return false;
}
// Validate NewValue for non-nullable reference types
if (args.NewValue is null && !typeof (T).IsValueType && !Nullable.GetUnderlyingType (typeof (T))?.IsValueType == true)
{
throw new InvalidOperationException ("NewValue cannot be null for non-nullable reference types.");
}
finalValue = args.NewValue;
ValueChangedEventArgs<T> changedArgs = new (currentValue, finalValue);
onChanged?.Invoke (changedArgs);
changedEvent?.Invoke (null, changedArgs);
return true;
}
}

View File

@@ -0,0 +1,129 @@
#nullable enable
namespace Terminal.Gui.App;
using System;
/// <summary>
/// Provides helper methods for executing single-phase and result-producing workflows in the Cancellable Work Pattern (CWP).
/// </summary>
/// <remarks>
/// <para>
/// Used for workflows that allow customization or cancellation, such as command execution
/// (e.g., <see cref="View.RaiseAccepting"/>) or scheme resolution (e.g., <see cref="View.GetScheme"/>).
/// The <see cref="Execute{T}"/> method handles workflows without results, while
/// <see cref="ExecuteWithResult{TResult}"/> handles workflows producing results.
/// </para>
/// </remarks>
/// <seealso cref="ResultEventArgs{T}"/>
public static class CWPWorkflowHelper
{
/// <summary>
/// Executes a single-phase CWP workflow with a virtual method, event, and optional default action.
/// </summary>
/// <typeparam name="T">The type of the result in the event arguments.</typeparam>
/// <param name="onMethod">The virtual method invoked first, returning true to mark the workflow as handled.</param>
/// <param name="eventHandler">The event handler to invoke, or null if no handlers are subscribed.</param>
/// <param name="args">The event arguments containing a result and handled status.</param>
/// <param name="defaultAction">The default action to execute if the workflow is not handled, or null if none.</param>
/// <returns>True if the workflow was handled, false if not, or null if no event handlers are subscribed.</returns>
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="onMethod"/> or <paramref name="args"/> is null.
/// </exception>
/// <example>
/// <code>
/// ResultEventArgs&lt;bool&gt; args = new();
/// Func&lt;ResultEventArgs&lt;bool&gt;, bool&gt; onAccepting = _ =&gt; false;
/// EventHandler&lt;ResultEventArgs&lt;bool&gt;&gt;? acceptingHandler = null;
/// Action? defaultAction = () =&gt; args.Result = true;
/// bool? handled = CWPWorkflowHelper.Execute(onAccepting, acceptingHandler, args, defaultAction);
/// </code>
/// </example>
public static bool? Execute<T> (
Func<ResultEventArgs<T>, bool> onMethod,
EventHandler<ResultEventArgs<T>>? eventHandler,
ResultEventArgs<T> args,
Action? defaultAction = null)
{
ArgumentNullException.ThrowIfNull (onMethod);
ArgumentNullException.ThrowIfNull (args);
bool handled = onMethod (args) || args.Handled;
if (handled)
{
return true;
}
eventHandler?.Invoke (null, args);
if (args.Handled)
{
return true;
}
if (defaultAction is {})
{
defaultAction ();
return true;
}
return eventHandler is null ? null : false;
}
/// <summary>
/// Executes a CWP workflow that produces a result, suitable for methods like <see cref="View.GetScheme"/>.
/// </summary>
/// <typeparam name="TResult">The type of the result, which may be a nullable reference type (e.g., <see cref="Scheme"/>?).</typeparam>
/// <param name="onMethod">The virtual method invoked first, returning true to mark the workflow as handled.</param>
/// <param name="eventHandler">The event handler to invoke, or null if no handlers are subscribed.</param>
/// <param name="args">The event arguments containing a result and handled status.</param>
/// <param name="defaultAction">The default action that produces the result if the workflow is not handled.</param>
/// <returns>The result from the event arguments or the default action.</returns>
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="onMethod"/>, <paramref name="args"/>, or <paramref name="defaultAction"/> is null.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown if <see cref="ResultEventArgs{T}.Result"/> is null for non-nullable reference types when <see cref="ResultEventArgs{T}.Handled"/> is true.
/// </exception>
/// <example>
/// <code>
/// ResultEventArgs&lt;Scheme?&gt; args = new();
/// Func&lt;ResultEventArgs&lt;Scheme?&gt;, bool&gt; onGettingScheme = _ =&gt; false;
/// EventHandler&lt;ResultEventArgs&lt;Scheme?&gt;&gt;? gettingSchemeHandler = null;
/// Func&lt;Scheme&gt; defaultAction = () =&gt; SchemeManager.GetScheme("Base");
/// Scheme scheme = CWPWorkflowHelper.ExecuteWithResult(onGettingScheme, gettingSchemeHandler, args, defaultAction);
/// </code>
/// </example>
public static TResult ExecuteWithResult<TResult> (
Func<ResultEventArgs<TResult>, bool> onMethod,
EventHandler<ResultEventArgs<TResult>>? eventHandler,
ResultEventArgs<TResult> args,
Func<TResult> defaultAction)
{
ArgumentNullException.ThrowIfNull (onMethod);
ArgumentNullException.ThrowIfNull (args);
ArgumentNullException.ThrowIfNull (defaultAction);
bool handled = onMethod (args) || args.Handled;
if (handled)
{
if (args.Result is null && !typeof (TResult).IsValueType && !Nullable.GetUnderlyingType (typeof (TResult))?.IsValueType == true)
{
throw new InvalidOperationException ("Result cannot be null for non-nullable reference types when Handled is true.");
}
return args.Result!;
}
eventHandler?.Invoke (null, args);
if (!args.Handled)
{
return defaultAction ();
}
if (args.Result is null && !typeof (TResult).IsValueType && !Nullable.GetUnderlyingType (typeof (TResult))?.IsValueType == true)
{
throw new InvalidOperationException ("Result cannot be null for non-nullable reference types when Handled is true.");
}
return args.Result!;
}
}

View File

@@ -1,19 +1,20 @@
#nullable enable
using System.ComponentModel;
namespace Terminal.Gui.ViewBase;
namespace Terminal.Gui.App;
#pragma warning disable CS1711
/// <summary>
/// <see cref="EventArgs"/> for events that convey changes to a property of type <typeparamref name="T"/>.
/// Provides data for events that can be cancelled without a changeable result in a cancellable workflow in the Cancellable Work Pattern (CWP).
/// </summary>
/// <typeparam name="T">The type of the value that was part of the change being canceled.</typeparam>
/// <remarks>
/// Events that use this class can be cancellable. Where applicable, the <see cref="CancelEventArgs.Cancel"/> property
/// should be set to
/// <see langword="true"/> to prevent the state change from occurring.
/// Used for workflows where a change (e.g., a simple property change) can be cancelled, but the
/// value being changed is not directly modified by the event handlers.
/// </remarks>
/// <typeparam name="T">The type of the value that is being changed.</typeparam>
/// <seealso cref="ValueChangingEventArgs{T}"/>
/// <seealso cref="ResultEventArgs{T}"/>
public class CancelEventArgs<T> : CancelEventArgs where T : notnull
{
/// <summary>Initializes a new instance of the <see cref="CancelEventArgs{T}"/> class.</summary>

View File

@@ -0,0 +1,22 @@
#nullable enable
namespace Terminal.Gui.App;
#pragma warning disable CS1711
/// <summary>
/// Provides data for events that convey the current value of a property or other value in a cancellable workflow (CWP).
/// </summary>
/// <remarks>
/// Used for workflows where the current value of a property or value is being conveyed, such as
/// when a property has been changed.
/// </remarks>
/// <typeparam name="T">The type of the value.</typeparam>
public class EventArgs<T> : EventArgs /*where T : notnull*/
{
/// <summary>Initializes a new instance of the <see cref="EventArgs{T}"/> class.</summary>
/// <param name="currentValue">The current value of the property.</param>
/// <typeparam name="T">The type of the value.</typeparam>
public EventArgs (in T currentValue) { Value = currentValue; }
/// <summary>The current value of the property.</summary>
public T Value { get; }
}

View File

@@ -0,0 +1,45 @@
#nullable enable
namespace Terminal.Gui.App;
using System;
#pragma warning disable CS1711
/// <summary>
/// Provides data for events that produce a result in a cancellable workflow in the Cancellable Work Pattern (CWP).
/// </summary>
/// <remarks>
/// Used for workflows where a result (e.g., <see cref="Command"/> outcome, <see cref="Attribute"/> resolution) is
/// being produced or cancelled, such as for methods like <see cref="View.GetAttributeForRole"/>.
/// </remarks>
/// <typeparam name="T">The type of the result.</typeparam>
/// <seealso cref="CancelEventArgs{T}"/>
/// <seealso cref="ValueChangingEventArgs{T}"/>
public class ResultEventArgs<T>
{
/// <summary>
/// Gets or sets the result of the operation, which may be null if no result is provided.
/// </summary>
public T? Result { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the operation has been handled.
/// If true, the operation is considered handled and may use the provided result.
/// </summary>
public bool Handled { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ResultEventArgs{T}"/> class with no initial result.
/// </summary>
public ResultEventArgs () { }
/// <summary>
/// Initializes a new instance of the <see cref="ResultEventArgs{T}"/> class with an initial result.
/// </summary>
/// <param name="result">The initial result, which may be null for optional outcomes.</param>
public ResultEventArgs (T? result)
{
Result = result;
}
}
#pragma warning restore CS1711

View File

@@ -0,0 +1,39 @@
#nullable enable
namespace Terminal.Gui.App;
/// <summary>
/// Provides data for events that notify of a completed property change in the Cancellable Work Pattern (CWP).
/// </summary>
/// <remarks>
/// <para>
/// Used in post-change events raised by <see cref="CWPPropertyHelper.ChangeProperty{T}"/> to notify
/// subscribers of a property change, such as in <see cref="OrientationHelper"/> when the
/// <see cref="Orientation"/> property is updated or <see cref="View.SchemeName"/> when the scheme name changes.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the property value, which may be a nullable reference type (e.g., <see cref="string"/>?).</typeparam>
/// <seealso cref="ValueChangingEventArgs{T}"/>
/// <seealso cref="CWPPropertyHelper"/>
public class ValueChangedEventArgs<T>
{
/// <summary>
/// Gets the value before the change, which may be null for nullable types.
/// </summary>
public T OldValue { get; }
/// <summary>
/// Gets the value after the change, which may be null for nullable types.
/// </summary>
public T NewValue { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ValueChangedEventArgs{T}"/> class.
/// </summary>
/// <param name="oldValue">The value before the change, which may be null for nullable types.</param>
/// <param name="newValue">The value after the change, which may be null for nullable types.</param>
public ValueChangedEventArgs (T oldValue, T newValue)
{
OldValue = oldValue;
NewValue = newValue;
}
}

View File

@@ -0,0 +1,44 @@
#nullable enable
namespace Terminal.Gui.App;
/// <summary>
/// Provides data for events that allow modification or cancellation of a property change in the Cancellable Work Pattern (CWP).
/// </summary>
/// <remarks>
/// <para>
/// Used in pre-change events raised by <see cref="CWPPropertyHelper.ChangeProperty{T}"/> to allow handlers to
/// modify the proposed value or cancel the change, such as for <see cref="View.SchemeName"/> or
/// <see cref="OrientationHelper"/>.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the property value, which may be a nullable reference type (e.g., <see cref="string"/>?).</typeparam>
/// <seealso cref="ValueChangedEventArgs{T}"/>
/// <seealso cref="CWPPropertyHelper"/>
public class ValueChangingEventArgs<T>
{
/// <summary>
/// Gets the current value before the change.
/// </summary>
public T CurrentValue { get; }
/// <summary>
/// Gets or sets the proposed new value, which can be modified by event handlers.
/// </summary>
public T NewValue { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the change has been handled. If true, the change is cancelled.
/// </summary>
public bool Handled { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ValueChangingEventArgs{T}"/> class.
/// </summary>
/// <param name="currentValue">The current value before the change, which may be null for nullable types.</param>
/// <param name="newValue">The proposed new value, which may be null for nullable types.</param>
public ValueChangingEventArgs (T currentValue, T newValue)
{
CurrentValue = currentValue;
NewValue = newValue;
}
}

View File

@@ -9,3 +9,15 @@ public class ConfigurationManagerEventArgs : EventArgs
public ConfigurationManagerEventArgs () { }
}
//public class ConfigurationLoadEventArgs : ResultEventArgs<SettingsScope>
//{
// public ConfigLocations Location { get; }
// public string? Path { get; }
// public ConfigurationLoadEventArgs (ConfigLocations location, string? path)
// {
// Location = location;
// Path = path;
// }
//}

View File

@@ -24,7 +24,7 @@ namespace Terminal.Gui.Configuration;
[JsonSerializable (typeof (AlignmentModes))]
[JsonSerializable (typeof (LineStyle))]
[JsonSerializable (typeof (ShadowStyle))]
[JsonSerializable (typeof (HighlightStyle))]
[JsonSerializable (typeof (MouseState))]
[JsonSerializable (typeof (TextStyle))]
[JsonSerializable (typeof (Dictionary<ColorName16, string>))]
[JsonSerializable (typeof (Dictionary<string, Color>))]

View File

@@ -296,12 +296,12 @@ public static class ThemeManager
internal static void OnThemeChanged (string previousThemeName, string newThemeName)
{
Logging.Debug ($"Themes.OnThemeChanged({previousThemeName}) -> {Theme}");
StringPropertyEventArgs args = new StringPropertyEventArgs (in previousThemeName, ref newThemeName!);
EventArgs<string> args = new (newThemeName);
ThemeChanged?.Invoke (null, args);
}
/// <summary>Raised when the selected theme has changed.</summary>
public static event EventHandler<StringPropertyEventArgs>? ThemeChanged;
public static event EventHandler<EventArgs<string>>? ThemeChanged;
/// <summary>
/// Validates all themes in the <see cref="Themes"/> dictionary.

View File

@@ -1,12 +0,0 @@
#nullable enable
namespace Terminal.Gui.Drawing;
/// <summary>Event arguments for the <see cref="Color"/> events.</summary>
public class ColorEventArgs : EventArgs<Color>
{
/// <summary>Initializes a new instance of <see cref="ColorEventArgs"/>
/// <paramref name="newColor"/>The value that is being changed to.</summary>
public ColorEventArgs (Color newColor) :base(newColor) { }
}

View File

@@ -493,7 +493,7 @@ public record Scheme : IEqualityOperators<Scheme, Scheme, bool>
private readonly Attribute? _highlight;
/// <summary>
/// The visual role for elements that are highlighted (e.g., when the mouse is hovering over a <see cref="Button"/>).
/// The visual role for elements that are highlighted (e.g., when the mouse is inside a <see cref="Button"/>).
/// If not explicitly set, will be a derived value. See the description for <see cref="Scheme"/> for details on the
/// algorithm used.
/// </summary>

View File

@@ -1,21 +0,0 @@
#nullable enable
using System.ComponentModel;
namespace Terminal.Gui.Drawing;
/// <summary>Event args for draw events</summary>
public class SchemeEventArgs : CancelEventArgs
{
/// <summary>Creates a new instance of the <see cref="SchemeEventArgs"/> class.</summary>
public SchemeEventArgs (in Scheme? currentScheme, ref Scheme? newScheme)
{
CurrentScheme = currentScheme;
NewScheme = newScheme;
}
/// <summary>Gets the View's current <see cref="Scheme"/>.</summary>
public Scheme? CurrentScheme { get; }
/// <summary>Gets or sets the View's new <see cref="Scheme"/>.</summary>
public Scheme? NewScheme { get; set; }
}

View File

@@ -43,7 +43,7 @@ public enum VisualRole
HotActive,
/// <summary>
/// The visual role for elements that are highlighted (e.g., when the mouse is hovering over a <see cref="Button"/>).
/// The visual role for elements that are highlighted (e.g., when the mouse is inside over a <see cref="Button"/>).
/// </summary>
Highlight,

View File

@@ -1,30 +1,63 @@
#nullable enable
namespace Terminal.Gui.Drawing;
/// <summary>Args for events that relate <see cref="VisualRole"/>.</summary>
public class VisualRoleEventArgs : CancelEventArgs<Attribute>
using System;
#pragma warning disable CS1711
/// <summary>
/// Provides data for cancellable workflow events that resolve an <see cref="Attribute"/> for a specific
/// <see cref="VisualRole"/> in the Cancellable Work Pattern (CWP).
/// </summary>
/// <remarks>
/// <para>
/// Used in events like <see cref="View.GettingAttributeForRole"/> to allow customization or cancellation
/// of attribute resolution for a <see cref="VisualRole"/>, such as determining the appearance of a
/// <see cref="View"/> based on its state (e.g., focused, disabled).
/// </para>
/// <para>
/// Inherits from <see cref="ResultEventArgs{T}"/> with <c>T = <see cref="Attribute"/></c>, providing a
/// cancellable result workflow where event handlers can supply a custom <see cref="Attribute"/> or mark
/// the operation as handled.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the result, constrained to <see cref="Attribute"/>.</typeparam>
/// <example>
/// <code>
/// View view = new();
/// view.GettingAttributeForRole += (sender, args) =>
/// {
/// if (args.Role == VisualRole.Focus)
/// {
/// args.Result = new Attribute(Color.BrightCyan, Color.Black);
/// args.Handled = true;
/// }
/// };
/// Attribute attribute = view.GetAttributeForRole(VisualRole.Focus);
/// </code>
/// </example>
/// <seealso cref="ResultEventArgs{T}"/>
/// <seealso cref="VisualRole"/>
/// <seealso cref="Attribute"/>
/// <seealso cref="View.GetAttributeForRole"/>
public class VisualRoleEventArgs : ResultEventArgs<Attribute?>
{
/// <inheritdoc/>
public VisualRoleEventArgs (in VisualRole role, ref readonly Attribute currentValue, ref Attribute newValue, bool cancel = false) : base (
in currentValue,
ref newValue,
cancel)
{
Role = role;
}
/// <inheritdoc/>
protected VisualRoleEventArgs (in VisualRole role, ref readonly Attribute currentValue, ref Attribute newValue) : base (currentValue, newValue)
{
Role = role;
}
/// <inheritdoc/>
public VisualRoleEventArgs (in VisualRole role, ref Attribute newValue) : base (default (Attribute), newValue) { Role = role; }
/// <summary>
/// Gets the <see cref="VisualRole"/> for which an <see cref="Attribute"/> is being resolved.
/// </summary>
public VisualRole Role { get; }
/// <summary>
/// The <see cref="VisualRole"/> that is being set.
/// Initializes a new instance of the <see cref="VisualRoleEventArgs"/> class with the specified
/// <see cref="VisualRole"/> and initial <see cref="Attribute"/> result.
/// </summary>
public VisualRole Role { get; set; }
/// <param name="role">The <see cref="VisualRole"/> for which the attribute is being resolved.</param>
/// <param name="result">The initial attribute result, which may be null if no result is provided.</param>
public VisualRoleEventArgs (in VisualRole role, Attribute? result)
: base (result)
{
Role = role;
}
}
#pragma warning restore CS1711

View File

@@ -184,7 +184,7 @@ public class MainLoop<T> : IMainLoop<T>
if (v.NeedsDraw || v.NeedsLayout)
{
Logging.Trace ($"{v.GetType ().Name} triggered redraw (NeedsDraw={v.NeedsDraw} NeedsLayout={v.NeedsLayout}) ");
// Logging.Trace ($"{v.GetType ().Name} triggered redraw (NeedsDraw={v.NeedsDraw} NeedsLayout={v.NeedsLayout}) ");
return true;
}

View File

@@ -921,17 +921,21 @@ internal class WindowsDriver : ConsoleDriver
}
await Task.Delay (delay);
var me = new MouseEventArgs
{
ScreenPosition = _pointMove,
Flags = mouseFlag
};
//Debug.WriteLine($"ProcessContinuousButtonPressedAsync: {view}");
if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0)
{
Point pointMove = _pointMove;
// TODO: This makes IConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
Application.Invoke (() => OnMouseEvent (me));
Application.Invoke (() =>
{
var me = new MouseEventArgs
{
ScreenPosition = pointMove,
Position = pointMove,
Flags = mouseFlag
};
OnMouseEvent (me);
});
}
}
}

View File

@@ -1,58 +1,78 @@
# Terminal.Gui Project
All files required to build the **Terminal.Gui** library (and NuGet package).
**Terminal.Gui** is a cross-platform UI toolkit for creating console-based graphical user interfaces in .NET. This repository contains all files required to build the **Terminal.Gui** library and NuGet package, enabling developers to create rich terminal applications with ease.
## Project Overview
**Terminal.Gui** provides a comprehensive framework for building interactive console applications with support for keyboard and mouse input, customizable views, and a robust event system. It is designed to work across Windows, macOS, and Linux, leveraging platform-specific console capabilities where available.
## Project Folder Structure
- `\` - The root folder contains the source code for the library.
- `Terminal.Gui.sln` - The Visual Studio solution
- `Application\` - The core `Application` logic, including `Application.cs`, which is is a `static` class that provides the base 'application engine', `RunState`, and `MainLoop`.
- `\` - The root folder contains the core solution and project files for the library.
- `Terminal.Gui.sln` - The Visual Studio solution file for the project.
- `Terminal.Gui.csproj` - The project file defining build configurations and dependencies.
- `App\` - Contains the core `Application` logic, including `Application.cs`, a `static` class that serves as the base 'application engine', managing `RunState` and `MainLoop`.
- `ConsoleDrivers\`
- `IConsoleDriver.cs` - Definition for the Console Driver API.
- Source files for the three `IConsoleDriver`-based drivers: .NET: `NetDriver`, Unix & Mac: `UnixDriver`, and Windows: `WindowsDriver`.
- `Configuration\` - Classes related to the `ConfigurationManager` for handling application settings.
- `Configuration\` - Classes related the `ConfigurationManager`.
- `Drivers\` - Contains the console driver implementations:
- `IConsoleDriver.cs` - Defines the Console Driver API.
- Driver implementations for .NET (`NetDriver`), Unix & macOS (`UnixDriver`), and Windows (`WindowsDriver`).
- `Clipboard\` - Classes related to clipboard access.
- `Drawing\` - Classes related to rendering graphical elements in the console.
- `Input\` - Classes relating to keyboard and mouse input.
- `Events.cs` - Defines keyboard and mouse-related structs & classes.
- etc...
- `FileServices\` - Utility classes for file operations and services.
- `Text\` - Classes related to text processing
- `Input\` - Classes handling keyboard and mouse input:
- `Events.cs` - Defines structs and classes for keyboard and mouse events.
- `Drawing\` - Classes related to drawing
- `Resources\` - Assets and resources used by the library.
- `View\` - The `View` class heirarchy, not including any sub-classes
- `Text\` - Classes for text processing and formatting.
- `View\` - Core `View` class hierarchy (excluding specific sub-classes):
- `View.cs` - The base class for non-modal visual elements such as controls.
- `Layout\`
- `PosDim.cs` - Implements *Computed Layout* system. These classes have deep dependencies on `View`.
- Related subdirectories for layout and positioning logic.
- `Views\` - Sub-classes of `View`
- `Toplevel` - Derived from `View`, the base class for modal visual elements such as top-level windows and dialogs. Supports the concept of `MenuBar` and `StatusBar`.
- `Window` - Derived from `TopLevel`; implements Toplevel views with a visible frame and Title.
- `Dialog` -
- etc...
- `ViewBase\` - Base classes and utilities for views.
- `FileServcies/` - File services classes.
- `Views\` - Specific sub-classes of `View`:
- `Toplevel` - Base class for modal visual elements like top-level windows and dialogs, supporting `MenuBar` and `StatusBar`.
- `Window` - Implements framed top-level views with titles.
- `Dialog` - Specialized windows for user interaction.
- Other specialized view classes.
## Version numbers
## Showcase
Version info for Terminal.Gui is managed by [gitversion](https://gitversion.net).
See the [Showcase](docs/showcase.md) to find independent applications and examples built with Terminal.Gui.
Install `gitversion`:
## Getting Started
For instructions on how to start using **Terminal.Gui**, refer to the [Getting Started Guide](https://gui-cs.github.io/Terminal.Gui/docs/getting-started.html) in our documentation.
## Documentation
Comprehensive documentation for **Terminal.Gui** is available at [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui). Key resources include:
- [Events Deep Dive](https://gui-cs.github.io/Terminal.Gui/docs/events.html) - Detailed guide on event handling and the Cancellable Work Pattern.
- [View Documentation](https://gui-cs.github.io/Terminal.Gui/docs/View.html) - Information on creating and customizing views.
- [Keyboard Handling](https://gui-cs.github.io/Terminal.Gui/docs/keyboard.html) - Guide to managing keyboard input.
- [Mouse Support](https://gui-cs.github.io/Terminal.Gui/docs/mouse.html) - Details on implementing mouse interactions.
- [Showcase](https://gui-cs.github.io/Terminal.Gui/docs/showcase.html) - A collection of applications and examples built with Terminal.Gui.
For information on generating and updating the API documentation locally, refer to the [DocFX README](../docfx/README.md) in the `docfx` folder.
## Versioning
Version information for Terminal.Gui is managed by [gitversion](https://gitversion.net). To install `gitversion`:
```powershell
dotnet tool install --global GitVersion.Tool
dotnet-gitversion
```
The project version (the nuget package and in `Terminal.Gui.dll`) is determined from the latest `git tag`.
The project version (used in the NuGet package and `Terminal.Gui.dll`) is determined from the latest `git tag`. The format of version numbers is `major.minor.patch.build.height` and follows [Semantic Versioning](https://semver.org/) rules.
The format of version numbers is `vmajor.minor.patch.build.height` and follows the [Semantic Versioning](https://semver.org/) rules.
To define a new version (e.g. with a higher `major`, `minor`, `patch`, or `build` value) tag a commit using `git tag`:
To define a new version, tag a commit using `git tag`:
```powershell
git tag v1.3.4-beta.5 -a -m "Release v1.3.4 Beta 5"
@@ -60,111 +80,81 @@ dotnet-gitversion /updateprojectfiles
dotnet build -c Release
```
**DO NOT COMMIT AFTER USING `/updateprojectfiles`!**
Doing so will update the `.csproj` files in your branch with version info, which we do not want.
**DO NOT COMMIT AFTER USING `/updateprojectfiles`!** Doing so will update the `.csproj` files in your branch with version info, which we do not want.
## Publishing a Release of Terminal.Gui
First, use the [Semantic Versioning](https://semver.org/) rules.to determine the new verison number.
To release a new version, follow these steps based on [Semantic Versioning](https://semver.org/) rules:
Given a version number MAJOR.MINOR.PATCH, increment the:
- **MAJOR** version for incompatible API changes.
- **MINOR** version for backwards-compatible functionality additions.
- **PATCH** version for backwards-compatible bug fixes.
* MAJOR version when you make incompatible API changes
* MINOR version when you add functionality in a backwards compatible manner
* PATCH version when you make backwards compatible bug fixes
### Steps for Release:
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
1. **Verify the `develop` branch is ready for release**:
- Ensure all changes are committed and pushed to the `develop` branch.
- Ensure your local `develop` branch is up-to-date with `upstream/develop`.
To release a new version (e.g. with a higher `major`, `minor`, or `patch` value) tag a commit using `git tag` and then push that tag directly to the `main` branch on `github.com/gui-cs/Terminal.Gui` (`upstream`).
2. **Create a pull request for the release in the `develop` branch**:
- Title the PR as "Release vX.Y.Z".
```powershell
git checkout develop
git pull upstream develop
git checkout -b vX_Y_Z
git add .
git commit -m "Release vX.Y.Z"
git push
```
- Go to the link printed by `git push` and fill out the Pull Request.
The `tag` must be of the form `v<major>.<minor>.<patch>`, e.g. `v2.3.4`.
3. **On github.com, verify the build action worked on your fork, then merge the PR**.
`patch` can indicate pre-release or not (e.g. `pre`, `beta`, `rc`, etc...).
4. **Pull the merged `develop` from `upstream`**:
```powershell
git checkout develop
git pull upstream develop
```
### 1) Verify the `develop` branch is ready for release
5. **Merge `develop` into `main`**:
```powershell
git checkout main
git pull upstream main
git merge develop
```
- Fix any merge errors.
* Ensure everything is committed and pushed to the `develop` branch
* Ensure your local `develop` branch is up-to-date with `upstream/develop`
6. **Create a new annotated tag for the release on `main`**:
```powershell
git tag vX.Y.Z -a -m "Release vX.Y.Z"
```
### 2) Create a pull request for the release in the `develop` branch
7. **Push the new tag to `main` on `upstream`**:
```powershell
git push --atomic upstream main vX.Y.Z
```
The PR title should be of the form "Release v2.3.4"
8. **Monitor Github Actions to ensure the NuGet publishing worked**:
- Check [GitHub Actions](https://github.com/gui-cs/Terminal.Gui/actions).
```powershell
git checkout develop
git pull upstream develop
git checkout -b v2_3_4
git add .
git commit -m "Release v2.3.4"
git push
```
9. **Check NuGet to see the new package version (wait a few minutes)**:
- Visit [NuGet Package](https://www.nuget.org/packages/Terminal.Gui).
Go to the link printed by `git push` and fill out the Pull Request.
10. **Add a new Release in Github**:
- Go to [GitHub Releases](https://github.com/gui-cs/Terminal.Gui/releases) and generate release notes with the list of PRs since the last release.
### 3) On github.com, verify the build action worked on your fork, then merge the PR
11. **Update the `develop` branch with the new version**:
```powershell
git checkout develop
git pull upstream develop
git merge main
git push upstream develop
```
### 4) Pull the merged `develop` from `upstream`
## NuGet
```powershell
git checkout develop
git pull upstream develop
```
### 5) Merge `develop` into `main`
```powershell
git checkout main
git pull upstream main
git merge develop
```
Fix any merge errors.
### 6) Create a new annotated tag for the release on `main`
```powershell
git tag v2.3.4 -a -m "Release v2.3.4"
```
### 7) Push the new tag to `main` on `upstream`
```powershell
git push --atomic upstream main v2.3.4
```
*See https://stackoverflow.com/a/3745250/297526*
### 8) Monitor Github Actions to ensure the Nuget publishing worked.
https://github.com/gui-cs/Terminal.Gui/actions
### 9) Check Nuget to see the new package version (wait a few minutes)
https://www.nuget.org/packages/Terminal.Gui
### 10) Add a new Release in Github: https://github.com/gui-cs/Terminal.Gui/releases
Generate release notes with the list of PRs since the last release.
### 11) Update the `develop` branch with the new version
```powershell
git checkout develop
git pull upstream develop
git merge main
git push upstream develop
```
## Nuget
https://www.nuget.org/packages/Terminal.Gui
When a new version tag is defined and merged into `main`, a Nuget package will be generated by a Github Action.
If the version is pre-release (includes a hyphen, e.g. `1.3.4-beta.5`) the Nuget package will be tagged as pre-release.
Miguel & Tig can hide defunct/old Nuget packages.
The official NuGet package for Terminal.Gui is available at [https://www.nuget.org/packages/Terminal.Gui](https://www.nuget.org/packages/Terminal.Gui). When a new version tag is defined and merged into `main`, a NuGet package is automatically generated by a GitHub Action. Pre-release versions (e.g., `1.3.4-beta.5`) are tagged as pre-release on NuGet.
## Contributing
See [CONTRIBUTING.md](https://github.com/gui-cs/Terminal.Gui/blob/master/CONTRIBUTING.md).
We welcome contributions from the community. For detailed guidelines on how to contribute, including coding style, unit tests, and pull request processes, please refer to [CONTRIBUTING.md](https://github.com/gui-cs/Terminal.Gui/blob/master/CONTRIBUTING.md).

View File

@@ -1,21 +0,0 @@
#nullable enable
using System.ComponentModel;
namespace Terminal.Gui.Text;
/// <summary>Event args for <see langword="string"/> type property events</summary>
public class StringPropertyEventArgs : CancelEventArgs
{
/// <summary>Creates a new instance of the <see cref="StringPropertyEventArgs"/> class.</summary>
public StringPropertyEventArgs (in string? currentString, ref string? newString)
{
CurrentString = currentString;
NewString = newString;
}
/// <summary>Gets the current <see cref="String"/>.</summary>
public string? CurrentString { get; }
/// <summary>Gets or sets the new <see cref="String"/>.</summary>
public string? NewString { get; set; }
}

View File

@@ -95,13 +95,12 @@ public class Adornment : View, IDesignable
return true;
}
/// <param name="scheme"></param>
/// <inheritdoc />
protected override bool OnSettingScheme (in Scheme? scheme)
protected override bool OnSettingScheme (ValueChangingEventArgs<Scheme?> args)
{
Parent?.SetNeedsDraw ();
_scheme = scheme;
_scheme = args.NewValue;
return false;
}

View File

@@ -35,6 +35,8 @@ public partial class Border
return false;
}
MouseState |= MouseState.Pressed;
// Add Commands and KeyBindings - Note it's ok these get added each time. KeyBindings are cleared in EndArrange()
AddArrangeModeKeyBindings ();
@@ -425,6 +427,8 @@ public partial class Border
// Debug.Assert (_arranging != ViewArrangement.Fixed);
Arranging = ViewArrangement.Fixed;
MouseState &= ~MouseState.Pressed;
Application.MouseEvent -= ApplicationOnMouseEvent;
if (Application.MouseGrabView == this && _dragPosition.HasValue)
@@ -496,8 +500,6 @@ public partial class Border
_dragPosition = mouseEvent.Position;
Application.GrabMouse (this);
SetPressedHighlight (HighlightStyle);
// Determine the mode based on where the click occurred
ViewArrangement arrangeMode = DetermineArrangeModeFromClick ();
EnterArrangeMode (arrangeMode);
@@ -522,7 +524,6 @@ public partial class Border
{
_dragPosition = null;
Application.UngrabMouse ();
SetPressedHighlight (HighlightStyle.None);
EndArrangeMode ();

View File

@@ -53,14 +53,10 @@ public partial class Border : Adornment
Application.GrabbingMouse += Application_GrabbingMouse;
Application.UnGrabbingMouse += Application_UnGrabbingMouse;
HighlightStyle |= HighlightStyle.Pressed;
ThicknessChanged += OnThicknessChanged;
}
// TODO: Move DrawIndicator out of Border and into View
private void OnThicknessChanged (object? sender, EventArgs e)
{
if (IsInitialized)
@@ -117,7 +113,15 @@ public partial class Border : Adornment
{
base.BeginInit ();
if (Parent is null)
{
return;
}
ShowHideDrawIndicator ();
HighlightStates |= (Parent.Arrangement != ViewArrangement.Fixed ? MouseState.Pressed : MouseState.None);
#if SUBVIEW_BASED_BORDER
if (Parent is { })
{
@@ -275,6 +279,7 @@ public partial class Border : Adornment
LineStyle lineStyle = LineStyle;
if (Settings.FastHasFlags (BorderSettings.Title))
{
if (Thickness.Top == 2)
@@ -332,9 +337,16 @@ public partial class Border : Adornment
bool drawBottom = Thickness.Bottom > 0 && Frame.Width > 1 && Frame.Height > 1;
bool drawRight = Thickness.Right > 0 && (Frame.Height > 1 || Thickness.Top == 0);
Attribute prevAttr = Driver?.GetAttribute () ?? Attribute.Default;
//Attribute prevAttr = Driver?.GetAttribute () ?? Attribute.Default;
SetAttributeForRole (VisualRole.Normal);
Attribute normalAttribute = GetAttributeForRole (VisualRole.Normal);
if (MouseState.HasFlag (MouseState.Pressed))
{
normalAttribute = GetAttributeForRole (VisualRole.Highlight);
}
SetAttribute (normalAttribute);
if (drawTop)
{
@@ -348,7 +360,7 @@ public partial class Border : Adornment
borderBounds.Width,
Orientation.Horizontal,
lineStyle,
Driver?.GetAttribute ()
normalAttribute
);
}
else
@@ -363,7 +375,7 @@ public partial class Border : Adornment
Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
Orientation.Horizontal,
lineStyle,
Driver?.GetAttribute ()
normalAttribute
);
}
@@ -377,7 +389,7 @@ public partial class Border : Adornment
Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
Orientation.Horizontal,
lineStyle,
Driver?.GetAttribute ()
normalAttribute
);
lc?.AddLine (
@@ -385,7 +397,7 @@ public partial class Border : Adornment
Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
Orientation.Horizontal,
lineStyle,
Driver?.GetAttribute ()
normalAttribute
);
}
@@ -396,7 +408,7 @@ public partial class Border : Adornment
2,
Orientation.Horizontal,
lineStyle,
Driver?.GetAttribute ()
normalAttribute
);
// Add a vert line for ╔╡
@@ -405,7 +417,7 @@ public partial class Border : Adornment
titleBarsLength,
Orientation.Vertical,
LineStyle.Single,
Driver?.GetAttribute ()
normalAttribute
);
// Add a vert line for ╞
@@ -420,7 +432,7 @@ public partial class Border : Adornment
titleBarsLength,
Orientation.Vertical,
LineStyle.Single,
Driver?.GetAttribute ()
normalAttribute
);
// Add the right hand line for ╞═════╗
@@ -435,7 +447,7 @@ public partial class Border : Adornment
borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
Orientation.Horizontal,
lineStyle,
Driver?.GetAttribute ()
normalAttribute
);
}
}
@@ -449,7 +461,7 @@ public partial class Border : Adornment
sideLineLength,
Orientation.Vertical,
lineStyle,
Driver?.GetAttribute ()
normalAttribute
);
}
#endif
@@ -461,7 +473,7 @@ public partial class Border : Adornment
borderBounds.Width,
Orientation.Horizontal,
lineStyle,
Driver?.GetAttribute ()
normalAttribute
);
}
@@ -472,11 +484,11 @@ public partial class Border : Adornment
sideLineLength,
Orientation.Vertical,
lineStyle,
Driver?.GetAttribute ()
normalAttribute
);
}
SetAttribute (prevAttr);
// SetAttribute (prevAttr);
// TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
if (Diagnostics.HasFlag (ViewDiagnosticFlags.Ruler))

View File

@@ -1,5 +1,6 @@
#nullable enable
using System.Runtime.InteropServices;
namespace Terminal.Gui.ViewBase;
@@ -32,11 +33,6 @@ public class Margin : Adornment
/// <inheritdoc/>
public Margin (View parent) : base (parent)
{
/* Do nothing; View.CreateAdornment requires a constructor that takes a parent */
// BUGBUG: We should not set HighlightStyle.Pressed here, but wherever it is actually needed
// HighlightStyle |= HighlightStyle.Pressed;
Highlight += Margin_Highlight;
SubViewLayout += Margin_LayoutStarted;
// Margin should not be focusable
@@ -81,7 +77,7 @@ public class Margin : Adornment
{
var view = stack.Pop ();
if (view.Margin?.GetCachedClip() != null)
if (view.Margin?.GetCachedClip () != null)
{
view.Margin.NeedsDraw = true;
Region? saved = GetClip ();
@@ -113,16 +109,10 @@ public class Margin : Adornment
}
ShadowStyle = base.ShadowStyle;
Parent.MouseStateChanged += OnParentOnMouseStateChanged;
}
///// <inheritdoc />
//protected override bool OnGettingScheme (out Scheme? scheme)
//{
// scheme = Parent?.SuperView?.GetScheme () ?? SchemeManager.GetScheme (Schemes.Base);
// return true;
//}
/// <inheritdoc/>
protected override bool OnClearingViewport ()
{
@@ -153,12 +143,12 @@ public class Margin : Adornment
/// <inheritdoc />
protected override bool OnDrawingText ()
{
return ViewportSettings.HasFlag(ViewportSettingsFlags.Transparent);
return ViewportSettings.HasFlag (ViewportSettingsFlags.Transparent);
}
#region Shadow
private bool _pressed;
// private bool _pressed;
private ShadowView? _bottomShadow;
private ShadowView? _rightShadow;
@@ -228,14 +218,22 @@ public class Margin : Adornment
set => base.ShadowStyle = SetShadow (value);
}
private void Margin_Highlight (object? sender, CancelEventArgs<HighlightStyle> e)
private void OnParentOnMouseStateChanged (object? sender, EventArgs<MouseState> args)
{
if (Thickness == Thickness.Empty || ShadowStyle == ShadowStyle.None)
if (sender is not View parent || Thickness == Thickness.Empty || ShadowStyle == ShadowStyle.None)
{
return;
}
if (_pressed && e.NewValue == HighlightStyle.None)
bool pressed = args.Value.HasFlag (MouseState.Pressed) && parent.HighlightStates.HasFlag(MouseState.Pressed);
bool pressedOutside = args.Value.HasFlag (MouseState.PressedOutside) && parent.HighlightStates.HasFlag (MouseState.PressedOutside); ;
if (pressedOutside)
{
pressed = false;
}
if (MouseState.HasFlag (MouseState.Pressed) && !pressed)
{
// If the view is pressed and the highlight is being removed, move the shadow back.
// Note, for visual effects reasons, we only move horizontally.
@@ -256,14 +254,14 @@ public class Margin : Adornment
_bottomShadow.Visible = true;
}
_pressed = false;
MouseState &= ~MouseState.Pressed;
return;
}
if (!_pressed && e.NewValue.HasFlag (HighlightStyle.Pressed))
if (!MouseState.HasFlag (MouseState.Pressed) && pressed)
{
// If the view is not pressed and we want highlight move the shadow
// If the view is not pressed, and we want highlight move the shadow
// Note, for visual effects reasons, we only move horizontally.
// TODO: Add a setting or flag that lets the view move vertically as well.
Thickness = new (
@@ -271,7 +269,7 @@ public class Margin : Adornment
Thickness.Top + PRESS_MOVE_VERTICAL,
Thickness.Right - PRESS_MOVE_HORIZONTAL,
Thickness.Bottom - PRESS_MOVE_VERTICAL);
_pressed = true;
MouseState |= MouseState.Pressed;
if (_rightShadow is { })
{

View File

@@ -0,0 +1,35 @@
#nullable enable
namespace Terminal.Gui.ViewBase;
/// <summary>
/// Provides data for events that allow cancellation of adornment drawing in the Cancellable Work Pattern (CWP).
/// </summary>
/// <remarks>
/// <para>
/// Used in events raised by <see cref="View.DoDrawAdornments"/> to allow handlers to cancel the drawing
/// of <see cref="View.Margin"/>, <see cref="View.Border"/>, and <see cref="View.Padding"/> adornments.
/// </para>
/// </remarks>
/// <seealso cref="View.DrawAdornments"/>
/// <seealso cref="CWPEventHelper"/>
public class DrawAdornmentsEventArgs
{
/// <summary>
/// Gets the draw context for tracking drawn regions, or null if not tracking.
/// </summary>
public DrawContext? Context { get; }
/// <summary>
/// Gets or sets a value indicating whether the adornment drawing is handled. If true, drawing is cancelled.
/// </summary>
public bool Handled { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="DrawAdornmentsEventArgs"/> class.
/// </summary>
/// <param name="context">The draw context, or null if not tracking.</param>
public DrawAdornmentsEventArgs (DrawContext? context)
{
Context = context;
}
}

View File

@@ -1,18 +0,0 @@
#nullable enable
namespace Terminal.Gui.ViewBase;
#pragma warning disable CS1711
/// <summary>
/// <see cref="EventArgs"/> for events that convey changes to a property of type <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The type of the value that was part of the change being canceled.</typeparam>
public class EventArgs<T> : EventArgs where T : notnull
{
/// <summary>Initializes a new instance of the <see cref="EventArgs{T}"/> class.</summary>
/// <param name="currentValue">The current value of the property.</param>
/// <typeparam name="T">The type of the value.</typeparam>
public EventArgs (in T currentValue) { CurrentValue = currentValue; }
/// <summary>The current value of the property.</summary>
public T CurrentValue { get; }
}

View File

@@ -1,31 +0,0 @@
using System.Text.Json.Serialization;
namespace Terminal.Gui.ViewBase;
/// <summary>
/// Describes the highlight style of a view when the mouse is over it.
/// </summary>
[JsonConverter (typeof (JsonStringEnumConverter<HighlightStyle>))]
[Flags]
public enum HighlightStyle
{
/// <summary>
/// No highlight.
/// </summary>
None = 0,
/// <summary>
/// The mouse is hovering over the view (but not pressed). See <see cref="View.MouseEnter"/>.
/// </summary>
Hover = 1,
/// <summary>
/// The mouse is pressed within the <see cref="View.Viewport"/>.
/// </summary>
Pressed = 2,
/// <summary>
/// The mouse is pressed but moved outside the <see cref="View.Viewport"/>.
/// </summary>
PressedOutside = 4
}

View File

@@ -0,0 +1,39 @@
using System.Text.Json.Serialization;
namespace Terminal.Gui.ViewBase;
/// <summary>
/// Used to describe the state of the mouse in relation to a <see cref="View"/> (<see cref="View.MouseState"/>) and to
/// specify visual effects,
/// such as highlighting a button when the mouse is over it or changing the appearance of a view when the mouse is
/// pressed (<see cref="View.HighlightStates"/>).
/// </summary>
/// <seealso cref="View.MouseState"/>
/// <seealso cref="View.HighlightStates"/>
[JsonConverter (typeof (JsonStringEnumConverter<MouseState>))]
[Flags]
public enum MouseState
{
/// <summary>
/// No mouse interaction with the view is occurring.
/// </summary>
None = 0,
/// <summary>
/// The mouse is in the <see cref="View.Viewport"/> (but not pressed). Set between the <see cref="View.MouseEnter"/>
/// and <see cref="View.MouseLeave"/> events.
/// </summary>
In = 1,
/// <summary>
/// The mouse is in the <see cref="View.Viewport"/> and is pressed.
/// </summary>
Pressed = 2,
/// <summary>
/// The mouse is outside the <see cref="View.Viewport"/> and is pressed. If
/// <see cref="View.WantContinuousButtonPressed"/> is true,
/// this flag is ignored so that the view remains in the pressed state until the mouse is released.
/// </summary>
PressedOutside = 4
}

View File

@@ -1,5 +1,7 @@
namespace Terminal.Gui.ViewBase;
// TODO: CWP: FocusChanging should use an event arg type derived from ResultEventArgs<bool> so that its more obvious
// TODO: the result can be changed.
/// <summary>The event arguments for <see cref="View.HasFocus"/> events.</summary>
public class HasFocusEventArgs : CancelEventArgs<bool>
{

View File

@@ -1,6 +1,8 @@
#nullable enable
namespace Terminal.Gui.ViewBase;
/// <summary>
/// Helper class for implementing <see cref="IOrientation"/>.
/// </summary>
@@ -135,3 +137,33 @@ public class OrientationHelper
/// </remarks>
public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
}
//public class OrientationHelper
//{
// private Orientation _orientation;
// public Orientation Orientation
// {
// get => _orientation;
// set
// {
// CWPPropertyHelper.ChangeProperty (
// currentValue: _orientation,
// newValue: ref value,
// onChanging: args => OnOrientationChanging (args),
// changingEvent: OrientationChanging,
// onChanged: args => OnOrientationChanged (args),
// changedEvent: OrientationChanged
// );
// _orientation = value;
// }
// }
// public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
// public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
// protected virtual bool OnOrientationChanging (CancelEventArgs<Orientation> args) => false;
// protected virtual void OnOrientationChanged (EventArgs<Orientation> args) { }
//}

View File

@@ -219,7 +219,7 @@ public partial class View // Command APIs
/// </returns>
protected bool? RaiseSelecting (ICommandContext? ctx)
{
Logging.Debug ($"{Title} ({ctx?.Source?.Title})");
//Logging.Debug ($"{Title} ({ctx?.Source?.Title})");
CommandEventArgs args = new () { Context = ctx };
// Best practice is to invoke the virtual method first.
@@ -266,7 +266,7 @@ public partial class View // Command APIs
protected bool? RaiseHandlingHotKey ()
{
CommandEventArgs args = new () { Context = new CommandContext<KeyBinding> { Command = Command.HotKey } };
Logging.Debug ($"{Title} ({args.Context?.Source?.Title})");
//Logging.Debug ($"{Title} ({args.Context?.Source?.Title})");
// Best practice is to invoke the virtual method first.
// This allows derived classes to handle the event and potentially cancel it.

View File

@@ -12,21 +12,21 @@ public partial class View
public Attribute GetCurrentAttribute () { return Driver?.GetAttribute () ?? Attribute.Default; }
/// <summary>
/// Gets the <see cref="Attribute"/> associated with a specified <see cref="Drawing.VisualRole"/>
/// from the <see cref="Drawing.Scheme"/>.
/// Gets the <see cref="Attribute"/> associated with a specified <see cref="VisualRole"/>
/// from the <see cref="Scheme"/>.
/// <para>
/// Raises <see cref="OnGettingAttributeForRole"/>/<see cref="GettingAttributeForRole"/>
/// which can cancel the default behavior, and optionally change the attribute in the event args.
/// </para>
/// <para>
/// If <see cref="Enabled"/> is <see langword="false"/>, <see cref="Drawing.VisualRole.Disabled"/>
/// If <see cref="Enabled"/> is <see langword="false"/>, <see cref="VisualRole.Disabled"/>
/// will be used instead of <paramref name="role"></paramref>.
/// To override this behavior use <see cref="OnGettingAttributeForRole"/>/<see cref="GettingAttributeForRole"/>
/// to cancel the method, and return a different attribute.
/// </para>
/// <para>
/// If <see cref="HighlightStyle"/> is not <see cref="HighlightStyle.None"/> and <see cref="MouseHovering"/> is <see langword="true"/>,
/// the <see cref="Drawing.VisualRole.Highlight"/> will be used instead of <paramref name="role"/>.
/// If <see cref="HighlightStates"/> is not <see cref="MouseState.None"/> and <see cref="MouseState"/> is <see cref="MouseState.In"/>
/// the <see cref="VisualRole.Highlight"/> will be used instead of <paramref name="role"/>.
/// To override this behavior use <see cref="OnGettingAttributeForRole"/>/<see cref="GettingAttributeForRole"/>
/// to cancel the method, and return a different attribute.
/// </para>
@@ -43,18 +43,21 @@ public partial class View
return schemeAttribute;
}
VisualRoleEventArgs args = new (role, newValue: ref schemeAttribute, currentValue: ref schemeAttribute);
VisualRoleEventArgs args = new (role, result: schemeAttribute);
GettingAttributeForRole?.Invoke (this, args);
if (args.Cancel)
if (args is { Handled: true, Result: { } })
{
// A handler may have changed the attribute
return args.NewValue;
return args.Result.Value;
}
if (HighlightStyle != HighlightStyle.None)
if (role != VisualRole.Disabled && HighlightStates != MouseState.None)
{
if (MouseHovering && HighlightStyle.HasFlag (HighlightStyle.Hover) && role != VisualRole.Highlight && role != VisualRole.Disabled)
// The default behavior for HighlightStates of MouseState.Over is to use the Highlight role
if (((HighlightStates.HasFlag (MouseState.In) && MouseState.HasFlag (MouseState.In))
|| (HighlightStates.HasFlag (MouseState.Pressed) && MouseState.HasFlag (MouseState.Pressed)))
&& role != VisualRole.Highlight && !HasFocus)
{
schemeAttribute = GetAttributeForRole (VisualRole.Highlight);
}

View File

@@ -8,53 +8,96 @@ public partial class View
private string? _schemeName;
/// <summary>
/// Gets or sets the name of the Scheme to use for this View. If set, it will override the scheme inherited from the
/// SuperView. If a Scheme was explicitly set (<see cref="HasScheme"/> is <see langword="true"/>),
/// this property will be ignored.
/// Gets or sets the name of the scheme to use for this <see cref="View"/>. If set, it overrides the scheme
/// inherited from the <see cref="SuperView"/>. If a scheme was explicitly set (<see cref="HasScheme"/> is
/// true), this property is ignored.
/// </summary>
/// <remarks>
/// <para>
/// Setting this property raises pre- and post-change events via <see cref="CWPPropertyHelper"/>,
/// allowing customization or cancellation of the change. The <see cref="SchemeNameChanging"/> event
/// is raised before the change, and <see cref="SchemeNameChanged"/> is raised after.
/// </para>
/// </remarks>
/// <value>The scheme name, or null if no scheme name is set.</value>
/// <seealso cref="SchemeNameChanging"/>
/// <seealso cref="SchemeNameChanged"/>
public string? SchemeName
{
get => _schemeName;
set
{
if (_schemeName == value)
bool changed = CWPPropertyHelper.ChangeProperty (
_schemeName,
value,
OnSchemeNameChanging,
SchemeNameChanging,
OnSchemeNameChanged,
SchemeNameChanged,
out string? finalValue);
if (changed)
{
return;
_schemeName = finalValue;
}
if (OnSettingSchemeName (in _schemeName, ref value))
{
_schemeName = value;
return;
}
StringPropertyEventArgs args = new (in _schemeName, ref value);
SettingSchemeName?.Invoke (this, args);
if (args.Cancel)
{
_schemeName = args.NewString;
return;
}
_schemeName = value;
}
}
/// <summary>
/// Called when the <see cref="Scheme"/> for the View is to be set.
/// Called before the <see cref="SchemeName"/> property changes, allowing subclasses to cancel or modify the change.
/// </summary>
/// <param name="currentName"></param>
/// <param name="newName"></param>
/// <returns><see langword="true"/> to stop default behavior.</returns>
protected virtual bool OnSettingSchemeName (in string? currentName, ref string? newName) { return false; }
/// <param name="args">The event arguments containing the current and proposed new scheme name.</param>
/// <returns>True to cancel the change, false to proceed.</returns>
protected virtual bool OnSchemeNameChanging (ValueChangingEventArgs<string?> args)
{
return false;
}
/// <summary>Raised when the <see cref="Scheme"/> for the View is to be set.</summary>
/// <returns>
/// Set <see cref="CancelEventArgs.Cancel"/> to <see langword="true"/> to stop default behavior.
/// </returns>
public event EventHandler<StringPropertyEventArgs>? SettingSchemeName;
/// <summary>
/// Called after the <see cref="SchemeName"/> property changes, allowing subclasses to react to the change.
/// </summary>
/// <param name="args">The event arguments containing the old and new scheme name.</param>
protected virtual void OnSchemeNameChanged (ValueChangedEventArgs<string?> args)
{
}
/// <summary>
/// Raised before the <see cref="SchemeName"/> property changes, allowing handlers to modify or cancel the change.
/// </summary>
/// <remarks>
/// Set <see cref="ValueChangingEventArgs{T}.Handled"/> to true to cancel the change or modify
/// <see cref="ValueChangingEventArgs{T}.NewValue"/> to adjust the proposed value.
/// </remarks>
/// <example>
/// <code>
/// view.SchemeNameChanging += (sender, args) =>
/// {
/// if (args.NewValue == "InvalidScheme")
/// {
/// args.Handled = true;
/// Console.WriteLine("Invalid scheme name cancelled.");
/// }
/// };
/// </code>
/// </example>
public event EventHandler<ValueChangingEventArgs<string?>>? SchemeNameChanging;
/// <summary>
/// Raised after the <see cref="SchemeName"/> property changes, notifying handlers of the completed change.
/// </summary>
/// <remarks>
/// Provides the old and new scheme name via <see cref="ValueChangedEventArgs{T}.OldValue"/> and
/// <see cref="ValueChangedEventArgs{T}.NewValue"/>, which may be null.
/// </remarks>
/// <example>
/// <code>
/// view.SchemeNameChanged += (sender, args) =>
/// {
/// Console.WriteLine($"SchemeName changed from {args.OldValue ?? "none"} to {args.NewValue ?? "none"}.");
/// };
/// </code>
/// </example>
public event EventHandler<ValueChangedEventArgs<string?>>? SchemeNameChanged;
// Both holds the set Scheme and is used to determine if a Scheme has been set or not
private Scheme? _scheme;
@@ -66,112 +109,165 @@ public partial class View
public bool HasScheme => _scheme is { };
/// <summary>
/// Gets the Scheme for the View. If the Scheme has not been explicitly set (see <see cref="HasScheme"/>), gets
/// <see cref="SuperView"/>'s Scheme.
/// Gets the scheme for the <see cref="View"/>. If the scheme has not been explicitly set
/// (see <see cref="HasScheme"/>), gets the <see cref="SuperView"/>'s scheme or falls back to the base scheme.
/// </summary>
/// <returns></returns>
/// <returns>The resolved scheme, never null.</returns>
/// <remarks>
/// <para>
/// This method uses the Cancellable Work Pattern (CWP) via <see cref="CWPWorkflowHelper.ExecuteWithResult{TResult}"/>
/// to allow customization or cancellation of scheme resolution through the <see cref="OnGettingScheme"/> method
/// and <see cref="GettingScheme"/> event.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// view.GettingScheme += (sender, args) =>
/// {
/// args.Result = SchemeManager.GetScheme("Custom");
/// args.Handled = true;
/// };
/// Scheme scheme = view.GetScheme();
/// </code>
/// </example>
public Scheme GetScheme ()
{
if (OnGettingScheme (out Scheme? newScheme))
ResultEventArgs<Scheme?> args = new ();
return CWPWorkflowHelper.ExecuteWithResult (
onMethod: args =>
{
bool cancelled = OnGettingScheme (out Scheme? newScheme);
args.Result = newScheme;
return cancelled;
},
eventHandler: GettingScheme,
args,
DefaultAction);
Scheme DefaultAction ()
{
return newScheme!;
if (!HasScheme && !string.IsNullOrEmpty (SchemeName))
{
return SchemeManager.GetScheme (SchemeName);
}
if (!HasScheme)
{
return SuperView?.GetScheme () ?? SchemeManager.GetScheme (Schemes.Base);
}
return _scheme!;
}
var args = new SchemeEventArgs (in _scheme, ref newScheme);
GettingScheme?.Invoke (this, args);
if (args.Cancel)
{
return args.NewScheme!;
}
if (!HasScheme && !string.IsNullOrEmpty (SchemeName))
{
return SchemeManager.GetScheme (SchemeName);
}
if (!HasScheme)
{
return SuperView?.GetScheme () ?? SchemeManager.GetScheme (Schemes.Base);
}
return _scheme!;
}
/// <summary>
/// Called when the <see cref="Scheme"/> for the View is being retrieved. Overrides can return <see langword="true"/>
/// to
/// stop further processing and optionally set <paramref name="scheme"/> to a different value.
/// Called when the <see cref="Scheme"/> for the <see cref="View"/> is being retrieved. Subclasses can return
/// true to stop further processing and optionally set <paramref name="scheme"/> to a different value.
/// </summary>
/// <returns><see langword="true"/> to stop default behavior.</returns>
/// <param name="scheme">The scheme to use, or null to continue processing.</param>
/// <returns>True to stop default behavior, false to proceed.</returns>
protected virtual bool OnGettingScheme (out Scheme? scheme)
{
scheme = null;
return false;
}
/// <summary>
/// Raised when the <see cref="Scheme"/> for the View is being retrieved. Overrides can return <see langword="true"/>
/// to
/// stop further processing and optionally set the <see cref="Scheme"/> in the event args to a different value.
/// Raised when the <see cref="Scheme"/> for the <see cref="View"/> is being retrieved. Handlers can set
/// <see cref="ResultEventArgs{T}.Handled"/> to true to stop further processing and optionally set
/// <see cref="ResultEventArgs{T}.Result"/> to a different value.
/// </summary>
/// <returns>
/// Set `Cancel` to <see langword="true"/> to stop default behavior.
/// </returns>
public event EventHandler<SchemeEventArgs>? GettingScheme;
public event EventHandler<ResultEventArgs<Scheme?>>? GettingScheme;
/// <summary>
/// Sets the Scheme for the View. Raises <see cref="SettingScheme"/> event before setting the scheme.
/// Sets the scheme for the <see cref="View"/>, marking it as explicitly set.
/// </summary>
/// <param name="scheme">
/// The scheme to set. If <see langword="null"/> <see cref="HasScheme"/> will be
/// <see langword="false"/>.
/// </param>
/// <returns><see langword="true"/> if the scheme was set.</returns>
/// <param name="scheme">The scheme to set, or null to clear the explicit scheme.</param>
/// <returns>True if the scheme was set, false if unchanged or cancelled.</returns>
/// <remarks>
/// <para>
/// This method uses the Cancellable Work Pattern (CWP) via <see cref="CWPPropertyHelper.ChangeProperty{T}"/>
/// to allow customization or cancellation of the scheme change through the <see cref="OnSettingScheme"/> method
/// and <see cref="SchemeChanging"/> event. The <see cref="SchemeChanged"/> event is raised after a successful change.
/// </para>
/// <para>
/// If set to null, <see cref="HasScheme"/> will be false, and the view will inherit the scheme from its
/// <see cref="SuperView"/> or fall back to the base scheme.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// view.SchemeChanging += (sender, args) =>
/// {
/// if (args.NewValue is null)
/// {
/// args.Handled = true;
/// Console.WriteLine("Null scheme cancelled.");
/// }
/// };
/// view.SchemeChanged += (sender, args) =>
/// {
/// Console.WriteLine($"Scheme changed to {args.NewValue?.Name ?? "none"}.");
/// };
/// bool set = view.SetScheme(SchemeManager.GetScheme("Base"));
/// </code>
/// </example>
public bool SetScheme (Scheme? scheme)
{
if (_scheme == scheme)
bool changed = CWPPropertyHelper.ChangeProperty (
_scheme,
scheme,
OnSettingScheme,
SchemeChanging,
OnSchemeChanged,
SchemeChanged,
out Scheme? finalValue);
if (changed)
{
return false;
_scheme = finalValue;
return true;
}
if (OnSettingScheme (in scheme))
{
return false;
}
var args = new CancelEventArgs ();
SettingScheme?.Invoke (this, args);
if (args.Cancel)
{
return false;
}
_scheme = scheme;
// BUGBUG: This should be in Border.cs somehow
if (Border is { } && Border.LineStyle != LineStyle.None && Border.HasScheme)
{
Border.SetScheme (_scheme);
}
SetNeedsDraw ();
return true;
return false;
}
/// <summary>
/// Called when the <see cref="Scheme"/> for the View is to be set.
/// Called before the scheme is set, allowing subclasses to cancel or modify the change.
/// </summary>
/// <param name="scheme"></param>
/// <returns><see langword="true"/> to stop default behavior.</returns>
protected virtual bool OnSettingScheme (in Scheme? scheme) { return false; }
/// <param name="args">The event arguments containing the current and proposed new scheme.</param>
/// <returns>True to cancel the change, false to proceed.</returns>
protected virtual bool OnSettingScheme (ValueChangingEventArgs<Scheme?> args)
{
return false;
}
/// <summary>
/// Called after the scheme is set, allowing subclasses to react to the change.
/// </summary>
/// <param name="args">The event arguments containing the old and new scheme.</param>
protected virtual void OnSchemeChanged (ValueChangedEventArgs<Scheme?> args)
{
SetNeedsDraw ();
}
/// <summary>
/// Raised before the scheme is set, allowing handlers to modify or cancel the change.
/// </summary>
/// <remarks>
/// Set <see cref="ValueChangingEventArgs{T}.Handled"/> to true to cancel the change or modify
/// <see cref="ValueChangingEventArgs{T}.NewValue"/> to adjust the proposed scheme.
/// </remarks>
public event EventHandler<ValueChangingEventArgs<Scheme?>>? SchemeChanging;
/// <summary>
/// Raised after the scheme is set, notifying handlers of the completed change.
/// </summary>
/// <remarks>
/// Provides the old and new scheme via <see cref="ValueChangedEventArgs{T}.OldValue"/> and
/// <see cref="ValueChangedEventArgs{T}.NewValue"/>, which may be null.
/// </remarks>
public event EventHandler<ValueChangedEventArgs<Scheme?>>? SchemeChanged;
/// <summary>Raised when the <see cref="Scheme"/> for the View is to be set.</summary>
/// <returns>
/// Set <see cref="CancelEventArgs.Cancel"/> to <see langword="true"/> to stop default behavior.
/// </returns>
public event EventHandler<CancelEventArgs>? SettingScheme;
}

View File

@@ -164,7 +164,7 @@ public partial class View // Drawing APIs
}
}
private void DoDrawAdornments (Region? originalClip)
internal void DoDrawAdornments (Region? originalClip)
{
if (this is Adornment)
{

View File

@@ -46,12 +46,6 @@ public partial class View // Mouse APIs
#region MouseEnterLeave
/// <summary>
/// Gets whether the mouse is currently hovering over the View's <see cref="Viewport"/>. Is <see langword="true"/> after
/// <see cref="MouseEnter"/> has been raised, and before <see cref="MouseLeave"/> is raised.
/// </summary>
public bool MouseHovering { get; internal set; }
/// <summary>
/// INTERNAL Called by <see cref="Application.RaiseMouseEvent"/> when the mouse moves over the View's
/// <see cref="Frame"/>.
@@ -86,9 +80,9 @@ public partial class View // Mouse APIs
return true;
}
MouseHovering = true;
MouseState |= MouseState.In;
if (HighlightStyle != HighlightStyle.None)
if (HighlightStates != MouseState.None)
{
SetNeedsDraw ();
}
@@ -96,36 +90,6 @@ public partial class View // Mouse APIs
return false;
}
/// <summary>
/// Gets the <see cref="Drawing.Scheme"/> to use when the view is highlighted. The highlight colorscheme
/// is based on the current <see cref="Drawing.Scheme"/>, using <see cref="Color.GetBrighterColor"/>.
/// </summary>
/// <remarks>The highlight scheme.</remarks>
public Scheme GetHighlightScheme ()
{
Scheme cs = GetScheme ();
return cs with
{
Normal = new (
GetAttributeForRole (VisualRole.Normal).Foreground.GetBrighterColor (),
GetAttributeForRole (VisualRole.Normal).Background,
GetAttributeForRole (VisualRole.Normal).Style),
HotNormal = new (
GetAttributeForRole (VisualRole.HotNormal).Foreground.GetBrighterColor (),
GetAttributeForRole (VisualRole.HotNormal).Background,
GetAttributeForRole (VisualRole.HotNormal).Style),
Focus = new (
GetAttributeForRole (VisualRole.Focus).Foreground.GetBrighterColor (),
GetAttributeForRole (VisualRole.Focus).Background,
GetAttributeForRole (VisualRole.Focus).Style),
HotFocus = new (
GetAttributeForRole (VisualRole.HotFocus).Foreground.GetBrighterColor (),
GetAttributeForRole (VisualRole.HotFocus).Background,
GetAttributeForRole (VisualRole.HotFocus).Style)
};
}
/// <summary>
/// Called when the mouse moves over the View's <see cref="Frame"/> and no other non-SubView occludes it.
/// <see cref="MouseLeave"/> will
@@ -143,7 +107,7 @@ public partial class View // Mouse APIs
/// Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
/// </para>
/// <para>
/// See <see cref="SetPressedHighlight"/> for more information.
/// See <see cref="MouseState"/> for more information.
/// </para>
/// </remarks>
/// <param name="eventArgs"></param>
@@ -174,7 +138,7 @@ public partial class View // Mouse APIs
/// prevents Views higher in the visible hierarchy from receiving Enter/Leave events.
/// </para>
/// <para>
/// See <see cref="SetPressedHighlight"/> for more information.
/// See <see cref="MouseState"/> for more information.
/// </para>
/// </remarks>
public event EventHandler<CancelEventArgs>? MouseEnter;
@@ -192,7 +156,7 @@ public partial class View // Mouse APIs
/// Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
/// </para>
/// <para>
/// See <see cref="SetPressedHighlight"/> for more information.
/// See <see cref="MouseState"/> for more information.
/// </para>
/// </remarks>
internal void NewMouseLeaveEvent ()
@@ -204,9 +168,11 @@ public partial class View // Mouse APIs
MouseLeave?.Invoke (this, EventArgs.Empty);
MouseHovering = false;
MouseState &= ~MouseState.In;
if (HighlightStyle != HighlightStyle.None)
// TODO: Should we also MouseState &= ~MouseState.Pressed; ??
if (HighlightStates != MouseState.None)
{
SetNeedsDraw ();
}
@@ -220,7 +186,7 @@ public partial class View // Mouse APIs
/// Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
/// </para>
/// <para>
/// See <see cref="SetPressedHighlight"/> for more information.
/// See <see cref="MouseState"/> for more information.
/// </para>
/// </remarks>
protected virtual void OnMouseLeave () { }
@@ -233,7 +199,7 @@ public partial class View // Mouse APIs
/// Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
/// </para>
/// <para>
/// See <see cref="SetPressedHighlight"/> for more information.
/// See <see cref="MouseState"/> for more information.
/// </para>
/// </remarks>
public event EventHandler? MouseLeave;
@@ -242,8 +208,13 @@ public partial class View // Mouse APIs
#region Low Level Mouse Events
/// <summary>Gets or sets whether the <see cref="View"/> wants continuous button pressed events.</summary>
public virtual bool WantContinuousButtonPressed { get; set; }
/// <summary>
/// Gets or sets whether the <see cref="View"/> wants continuous button pressed events. When set to
/// <see langword="true"/>,
/// and the user presses and holds the mouse button, <see cref="NewMouseEvent"/> will be
/// repeatedly called with the same <see cref="MouseFlags"/> for as long as the mouse button remains pressed.
/// </summary>
public bool WantContinuousButtonPressed { get; set; }
/// <summary>Gets or sets whether the <see cref="View"/> wants mouse position reports.</summary>
/// <value><see langword="true"/> if mouse position reports are wanted; otherwise, <see langword="false"/>.</value>
@@ -263,13 +234,9 @@ public partial class View // Mouse APIs
/// mouse buttons was clicked, the <see cref="RaiseMouseClickEvent"/>/<see cref="MouseClick"/> event will be raised
/// </para>
/// <para>
/// See <see cref="SetPressedHighlight"/> for more information.
/// </para>
/// <para>
/// If <see cref="WantContinuousButtonPressed"/> is <see langword="true"/>, the <see cref="RaiseMouseEvent"/>/
/// <see cref="MouseEvent"/> event
/// will be raised on any new mouse event where <see cref="MouseEventArgs.Flags"/> indicates a button
/// is pressed.
/// If <see cref="WantContinuousButtonPressed"/> is <see langword="true"/>, and the user presses and holds the
/// mouse button, <see cref="NewMouseEvent"/> will be repeatedly called with the same <see cref="MouseFlags"/> for
/// as long as the mouse button remains pressed.
/// </para>
/// </remarks>
/// <param name="mouseEvent"></param>
@@ -300,7 +267,7 @@ public partial class View // Mouse APIs
}
// Post-Conditions
if (HighlightStyle != HighlightStyle.None || WantContinuousButtonPressed)
if (HighlightStates != MouseState.None || WantContinuousButtonPressed)
{
if (WhenGrabbedHandlePressed (mouseEvent))
{
@@ -318,7 +285,7 @@ public partial class View // Mouse APIs
}
}
// We get here if the view did not handle the mouse event via OnMouseEvent/MouseEvent and
// We get here if the view did not handle the mouse event via OnMouseEvent/MouseEvent, and
// it did not handle the press/release/clicked events via HandlePress/HandleRelease/HandleClicked
if (mouseEvent.IsSingleDoubleOrTripleClicked)
{
@@ -375,7 +342,7 @@ public partial class View // Mouse APIs
/// <summary>
/// INTERNAL For cases where the view is grabbed and the mouse is clicked, this method handles the released event
/// (typically
/// when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStyle"/> are set).
/// when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStates"/> are set).
/// </summary>
/// <remarks>
/// Marked internal just to support unit tests
@@ -390,7 +357,9 @@ public partial class View // Mouse APIs
{
if (Application.MouseGrabView == this)
{
SetPressedHighlight (HighlightStyle.None);
//Logging.Debug ($"{Id} - {MouseState}");
MouseState &= ~MouseState.Pressed;
MouseState &= ~MouseState.PressedOutside;
}
return mouseEvent.Handled = true;
@@ -402,7 +371,7 @@ public partial class View // Mouse APIs
/// <summary>
/// INTERNAL For cases where the view is grabbed and the mouse is clicked, this method handles the released event
/// (typically
/// when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStyle"/> are set).
/// when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStates"/> are set).
/// </summary>
/// <remarks>
/// <para>
@@ -433,19 +402,26 @@ public partial class View // Mouse APIs
if (Viewport.Contains (mouseEvent.Position))
{
if (this is not Adornment
&& SetPressedHighlight (HighlightStyle.HasFlag (HighlightStyle.Pressed) ? HighlightStyle.Pressed : HighlightStyle.None))
//Logging.Debug ($"{Id} - Inside Viewport: {MouseState}");
// The mouse is inside.
if (HighlightStates.HasFlag (MouseState.Pressed))
{
return true;
MouseState |= MouseState.Pressed;
}
}
else
{
if (this is not Adornment
&& SetPressedHighlight (HighlightStyle.HasFlag (HighlightStyle.PressedOutside) ? HighlightStyle.PressedOutside : HighlightStyle.None))
// Always clear PressedOutside when the mouse is pressed inside the Viewport
MouseState &= ~MouseState.PressedOutside;
}
if (!Viewport.Contains (mouseEvent.Position))
{
// Logging.Debug ($"{Id} - Outside Viewport: {MouseState}");
// The mouse is outside.
// When WantContinuousButtonPressed is set we want to keep the mouse state as pressed (e.g. a repeating button).
// This shows the user that the button is doing something, even if the mouse is outside the Viewport.
if (HighlightStates.HasFlag (MouseState.PressedOutside) && !WantContinuousButtonPressed)
{
return true;
MouseState |= MouseState.PressedOutside;
}
}
@@ -486,7 +462,6 @@ public partial class View // Mouse APIs
}
// Cancellable event
if (OnMouseClick (args) || args.Handled)
{
return args.Handled;
@@ -540,7 +515,7 @@ public partial class View // Mouse APIs
/// <summary>
/// INTERNAL For cases where the view is grabbed and the mouse is clicked, this method handles the click event
/// (typically
/// when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStyle"/> are set).
/// when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStates"/> are set).
/// </summary>
/// <remarks>
/// Marked internal just to support unit tests
@@ -556,10 +531,10 @@ public partial class View // Mouse APIs
// We're grabbed. Clicked event comes after the last Release. This is our signal to ungrab
Application.UngrabMouse ();
if (SetPressedHighlight (HighlightStyle.None))
{
return true;
}
// TODO: Prove we need to unset MouseState.Pressed and MouseState.PressedOutside here
// TODO: There may be perf gains if we don't unset these flags here
MouseState &= ~MouseState.Pressed;
MouseState &= ~MouseState.PressedOutside;
// If mouse is still in bounds, generate a click
if (!WantMousePositionReports && Viewport.Contains (mouseEvent.Position))
@@ -626,90 +601,83 @@ public partial class View // Mouse APIs
#endregion Mouse Wheel Events
#region Highlight Handling
#region MouseState Handling
private MouseState _mouseState;
/// <summary>
/// Gets or sets whether the <see cref="View"/> will be highlighted visually by mouse interaction.
/// Gets the state of the mouse relative to the View. When changed, the <see cref="MouseStateChanged"/>/
/// <see cref="OnMouseStateChanged"/>
/// event will be raised.
/// </summary>
public HighlightStyle HighlightStyle { get; set; }
/// <summary>
/// INTERNAL Raises the <see cref="Highlight"/> event. Returns <see langword="true"/> if the event was handled,
/// <see langword="false"/> otherwise.
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private bool RaiseHighlight (CancelEventArgs<HighlightStyle> args)
public MouseState MouseState
{
if (OnHighlight (args))
get => _mouseState;
internal set
{
return true;
if (_mouseState == value)
{
return;
}
EventArgs<MouseState> args = new (value);
RaiseMouseStateChanged (args);
_mouseState = value;
}
Highlight?.Invoke (this, args);
return args.Cancel;
}
/// <summary>
/// Called when the view is to be highlighted. The <see cref="HighlightStyle"/> passed in the event indicates the
/// highlight style that will be applied. The view can modify the highlight style by setting the
/// <see cref="CancelEventArgs{T}.NewValue"/> property.
/// </summary>
/// <param name="args">
/// Set the <see cref="CancelEventArgs{T}.NewValue"/> property to <see langword="true"/>, to cancel, indicating custom
/// highlighting.
/// </param>
/// <returns><see langword="true"/>, to cancel, indicating custom highlighting.</returns>
protected virtual bool OnHighlight (CancelEventArgs<HighlightStyle> args) { return false; }
/// <summary>
/// Raised when the view is to be highlighted. The <see cref="HighlightStyle"/> passed in the event indicates the
/// highlight style that will be applied. The view can modify the highlight style by setting the
/// <see cref="CancelEventArgs{T}.NewValue"/> property.
/// Set to <see langword="true"/>, to cancel, indicating custom highlighting.
/// </summary>
public event EventHandler<CancelEventArgs<HighlightStyle>>? Highlight;
/// <summary>
/// INTERNAL Enables the highlight for the view when the mouse is pressed. Called from OnMouseEvent.
/// Gets or sets which <see cref="MouseState"/> changes should cause the View to change its appearance.
/// </summary>
/// <remarks>
/// <para>
/// Set <see cref="HighlightStyle"/> to <see cref="HighlightStyle.Pressed"/> and/or
/// <see cref="HighlightStyle.PressedOutside"/> to enable.
/// <see cref="MouseState.In"/> is set by default, which means the View will be highlighted when the
/// mouse is over it. The default behavior of <see cref="SetAttributeForRole"/>
/// is to use the <see cref="Drawing.VisualRole.Highlight"/> role for the highlight Attribute.
/// </para>
/// <para>
/// Calls <see cref="OnHighlight"/> and raises the <see cref="Highlight"/> event.
/// <see cref="MouseState.Pressed"/> means the View will be highlighted when the mouse is pressed over it.
/// <see cref="Border"/>'s default behavior is to use
/// the <see cref="VisualRole.Highlight"/> role when the Border is pressed for Arrangement.
/// <see cref="Margin"/>'s default behavior, when shadows are enabled, is to move the shadow providing
/// a pressed effect.
/// </para>
/// <para>
/// Marked internal just to support unit tests
/// <see cref="MouseState.PressedOutside"/> means the View will be highlighted when the mouse was pressed
/// inside it and then moved outside of it, unless <see cref="WantContinuousButtonPressed"/> is set to
/// <see langword="true"/>, in which case the flag has no effect.
/// </para>
/// </remarks>
/// <returns><see langword="true"/>, if the Highlight event was handled, <see langword="false"/> otherwise.</returns>
internal bool SetPressedHighlight (HighlightStyle newHighlightStyle)
public MouseState HighlightStates { get; set; }
/// <summary>
/// INTERNAL Raises the <see cref="MouseStateChanged"/> event.
/// </summary>
/// <param name="args"></param>
private void RaiseMouseStateChanged (EventArgs<MouseState> args)
{
// TODO: Make the highlight colors configurable
if (!CanFocus)
{
return false;
}
//Logging.Debug ($"{Id} - {args.Value} -> {args.Value}");
HighlightStyle copy = HighlightStyle;
CancelEventArgs<HighlightStyle> args = new (ref copy, ref newHighlightStyle);
OnMouseStateChanged (args);
if (RaiseHighlight (args) || args.Cancel)
{
return true;
}
// For 3D Pressed Style - Note we don't care about canceling the event here
Margin?.RaiseHighlight (args);
return args.Cancel;
MouseStateChanged?.Invoke (this, args);
}
#endregion Highlight Handling
/// <summary>
/// Called when <see cref="MouseState"/> has changed, indicating the View should be highlighted or not. The <see cref="MouseState"/> passed in the event
/// indicates the highlight style that will be applied.
/// </summary>
protected virtual void OnMouseStateChanged (EventArgs<MouseState> args) { }
/// <summary>
/// RaisedCalled when <see cref="MouseState"/> has changed, indicating the View should be highlighted or not. The <see cref="MouseState"/> passed in the event
/// indicates the highlight style that will be applied.
/// </summary>
public event EventHandler<EventArgs<MouseState>>? MouseStateChanged;
#endregion MouseState Handling
private void DisposeMouse () { }
}

View File

@@ -637,6 +637,8 @@ public partial class View // Focus and cross-view navigation management (TabStop
return (true, false);
}
// TODO: CWP: FocusChanging should use an event arg type derived from ResultEventArgs<bool> so that its more obvious
// TODO: the result can be changed.
private bool RaiseFocusChanging (bool currentHasFocus, bool newHasFocus, View? currentFocused, View? newFocused)
{
Debug.Assert (currentFocused is null || currentFocused is { HasFocus: true });
@@ -882,6 +884,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
SetNeedsDraw ();
}
// TODO: CWP: FocusChanged should not be using event args derived from CancelEventArgs, as it is not cancellable.
private void RaiseFocusChanged (bool newHasFocus, View? previousFocusedView, View? focusedView)
{
// If we are the most focused view, we need to set the focused view in Application.Navigation

View File

@@ -139,7 +139,7 @@ public partial class View
{
Viewport = Viewport with
{
Y = Math.Min (args.CurrentValue, scrollBar.ScrollableContentSize - scrollBar.VisibleContentSize)
Y = Math.Min (args.Value, scrollBar.ScrollableContentSize - scrollBar.VisibleContentSize)
};
};
@@ -160,7 +160,7 @@ public partial class View
{
Viewport = Viewport with
{
X = Math.Min (args.CurrentValue, scrollBar.ScrollableContentSize - scrollBar.VisibleContentSize)
X = Math.Min (args.Value, scrollBar.ScrollableContentSize - scrollBar.VisibleContentSize)
};
};

View File

@@ -39,13 +39,13 @@ public class Button : View, IDesignable
/// Gets or sets the default Highlight Style.
/// </summary>
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static HighlightStyle DefaultHighlightStyle { get; set; } = HighlightStyle.Pressed | HighlightStyle.Hover;
public static MouseState DefaultHighlightStates { get; set; } = MouseState.In | MouseState.Pressed | MouseState.PressedOutside;
/// <summary>Initializes a new instance of <see cref="Button"/>.</summary>
public Button ()
{
TextAlignment = Alignment.Center;
VerticalTextAlignment = Alignment.Center;
base.TextAlignment = Alignment.Center;
base.VerticalTextAlignment = Alignment.Center;
_leftBracket = Glyphs.LeftBracket;
_rightBracket = Glyphs.RightBracket;
@@ -67,8 +67,8 @@ public class Button : View, IDesignable
TitleChanged += Button_TitleChanged;
MouseClick += Button_MouseClick;
ShadowStyle = DefaultShadow;
HighlightStyle = DefaultHighlightStyle;
base.ShadowStyle = DefaultShadow;
HighlightStates = DefaultHighlightStates;
}
private bool? HandleHotKeyCommand (ICommandContext commandContext)
@@ -99,33 +99,6 @@ public class Button : View, IDesignable
return false;
}
private bool _wantContinuousButtonPressed;
/// <inheritdoc/>
public override bool WantContinuousButtonPressed
{
get => _wantContinuousButtonPressed;
set
{
if (value == _wantContinuousButtonPressed)
{
return;
}
_wantContinuousButtonPressed = value;
if (_wantContinuousButtonPressed)
{
HighlightStyle |= HighlightStyle.PressedOutside;
}
else
{
HighlightStyle &= ~HighlightStyle.PressedOutside;
}
}
}
private void Button_MouseClick (object sender, MouseEventArgs e)
{
if (e.Handled)
@@ -139,7 +112,7 @@ public class Button : View, IDesignable
private void Button_TitleChanged (object sender, EventArgs<string> e)
{
base.Text = e.CurrentValue;
base.Text = e.Value;
TextFormatter.HotKeySpecifier = HotKeySpecifier;
}

View File

@@ -106,10 +106,10 @@ public class CharMap : View, IDesignable
{
if (e.Role != VisualRole.Focus && e.Role != VisualRole.Active)
{
e.NewValue = GetAttributeForRole (HasFocus ? VisualRole.Focus : VisualRole.Active);
e.Result = GetAttributeForRole (HasFocus ? VisualRole.Focus : VisualRole.Active);
}
e.Cancel = true;
e.Handled = true;
}
private bool? Move (ICommandContext? commandContext, int cpOffset)
@@ -442,9 +442,6 @@ public class CharMap : View, IDesignable
#region Mouse Handling
// TODO: Use this to demonstrate using a popover to show glyph info on hover
// public event EventHandler<ListViewItemEventArgs>? Hover;
private bool? HandleSelectCommand (ICommandContext? commandContext)
{
Point position = GetCursor (SelectedCodePoint);

View File

@@ -14,7 +14,7 @@ public class CheckBox : View
/// Gets or sets the default Highlight Style.
/// </summary>
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static HighlightStyle DefaultHighlightStyle { get; set; } = HighlightStyle.PressedOutside | HighlightStyle.Pressed | HighlightStyle.Hover;
public static MouseState DefaultHighlightStates { get; set; } = MouseState.PressedOutside | MouseState.Pressed | MouseState.In;
/// <summary>
/// Initializes a new instance of <see cref="CheckBox"/>.
@@ -46,7 +46,7 @@ public class CheckBox : View
TitleChanged += Checkbox_TitleChanged;
HighlightStyle = DefaultHighlightStyle;
HighlightStates = DefaultHighlightStates;
}
private bool? AdvanceAndSelect (ICommandContext? commandContext)
@@ -68,7 +68,7 @@ public class CheckBox : View
private void Checkbox_TitleChanged (object? sender, EventArgs<string> e)
{
base.Text = e.CurrentValue;
base.Text = e.Value;
TextFormatter.HotKeySpecifier = HotKeySpecifier;
}
@@ -152,7 +152,7 @@ public class CheckBox : View
return null;
}
CancelEventArgs<CheckState> e = new (in _checkedState, ref value);
ResultEventArgs<CheckState> e = new (value);
if (OnCheckedStateChanging (e))
{
@@ -161,9 +161,9 @@ public class CheckBox : View
CheckedStateChanging?.Invoke (this, e);
if (e.Cancel)
if (e.Handled)
{
return e.Cancel;
return e.Handled;
}
_checkedState = value;
@@ -184,7 +184,7 @@ public class CheckBox : View
/// The state change can be cancelled by setting the args.Cancel to <see langword="true"/>.
/// </para>
/// </remarks>
protected virtual bool OnCheckedStateChanging (CancelEventArgs<CheckState> args) { return false; }
protected virtual bool OnCheckedStateChanging (ResultEventArgs<CheckState> args) { return false; }
/// <summary>Raised when the <see cref="CheckBox"/> state is changing.</summary>
/// <remarks>
@@ -192,7 +192,7 @@ public class CheckBox : View
/// This event can be cancelled. If cancelled, the <see cref="CheckBox"/> will not change its state.
/// </para>
/// </remarks>
public event EventHandler<CancelEventArgs<CheckState>>? CheckedStateChanging;
public event EventHandler<ResultEventArgs<CheckState>>? CheckedStateChanging;
/// <summary>Called when the <see cref="CheckBox"/> state has changed.</summary>
protected virtual void OnCheckedStateChanged (EventArgs<CheckState> args) { }
@@ -221,32 +221,32 @@ public class CheckBox : View
public bool? AdvanceCheckState ()
{
CheckState oldValue = CheckedState;
CancelEventArgs<CheckState> e = new (in _checkedState, ref oldValue);
ResultEventArgs<CheckState> e = new (oldValue);
switch (CheckedState)
{
case CheckState.None:
e.NewValue = CheckState.Checked;
e.Result = CheckState.Checked;
break;
case CheckState.Checked:
e.NewValue = CheckState.UnChecked;
e.Result = CheckState.UnChecked;
break;
case CheckState.UnChecked:
if (AllowCheckStateNone)
{
e.NewValue = CheckState.None;
e.Result = CheckState.None;
}
else
{
e.NewValue = CheckState.Checked;
e.Result = CheckState.Checked;
}
break;
}
bool? cancelled = ChangeCheckedState (e.NewValue);
bool? cancelled = ChangeCheckedState (e.Result);
return cancelled;
}

View File

@@ -53,7 +53,7 @@ public class ColorPicker16 : View
}
/// <summary>Fired when a color is picked.</summary>
public event EventHandler<ColorEventArgs>? ColorChanged;
public event EventHandler<ResultEventArgs<Color>>? ColorChanged;
/// <summary>Cursor for the selected color.</summary>
public Point Cursor
@@ -280,7 +280,7 @@ public class ColorPicker16 : View
private void SetInitialProperties ()
{
HighlightStyle = HighlightStyle.PressedOutside | HighlightStyle.Pressed;
HighlightStates = ViewBase.MouseState.PressedOutside | ViewBase.MouseState.Pressed;
CanFocus = true;
AddCommands ();

View File

@@ -96,7 +96,7 @@ public partial class ColorPicker : View, IDesignable
/// <summary>
/// Fired when color is changed.
/// </summary>
public event EventHandler<ColorEventArgs>? ColorChanged;
public event EventHandler<ResultEventArgs<Color>>? ColorChanged;
/// <inheritdoc/>
protected override bool OnDrawingContent ()

View File

@@ -111,10 +111,10 @@ public class ComboBox : View, IDesignable
}
/// <inheritdoc />
protected override bool OnSettingScheme (in Scheme scheme)
protected override bool OnSettingScheme (ValueChangingEventArgs<Scheme?> args)
{
_listview.SetScheme(scheme);
return base.OnSettingScheme (in scheme);
_listview.SetScheme(args.NewValue);
return base.OnSettingScheme (args);
}
/// <summary>Gets or sets if the drop-down list can be hide with a button click event.</summary>

View File

@@ -0,0 +1 @@

View File

@@ -307,7 +307,7 @@ public class FlagSelector : View, IOrientation, IDesignable
Title = nameWithHotKey,
Id = name,
Data = flag,
HighlightStyle = HighlightStyle.Hover
HighlightStates = ViewBase.MouseState.In
};
checkbox.GettingAttributeForRole += (_, e) =>
@@ -320,36 +320,36 @@ public class FlagSelector : View, IOrientation, IDesignable
switch (e.Role)
{
case VisualRole.Normal:
e.Cancel = true;
e.Handled = true;
if (!HasFocus)
{
e.NewValue = GetAttributeForRole (VisualRole.Focus);
e.Result = GetAttributeForRole (VisualRole.Focus);
}
else
{
// If _scheme was set, it's because of Hover
if (checkbox.HasScheme)
{
e.NewValue = checkbox.GetAttributeForRole (VisualRole.Normal);
e.Result = checkbox.GetAttributeForRole (VisualRole.Normal);
}
else
{
e.NewValue = GetAttributeForRole (VisualRole.Normal);
e.Result = GetAttributeForRole (VisualRole.Normal);
}
}
break;
case VisualRole.HotNormal:
e.Cancel = true;
e.Handled = true;
if (!HasFocus)
{
e.NewValue = GetAttributeForRole (VisualRole.HotFocus);
e.Result = GetAttributeForRole (VisualRole.HotFocus);
}
else
{
e.NewValue = GetAttributeForRole (VisualRole.HotNormal);
e.Result = GetAttributeForRole (VisualRole.HotNormal);
}
break;

View File

@@ -40,7 +40,7 @@ public class Label : View, IDesignable
private void Label_TitleChanged (object sender, EventArgs<string> e)
{
base.Text = e.CurrentValue;
base.Text = e.Value;
TextFormatter.HotKeySpecifier = HotKeySpecifier;
}

View File

@@ -198,7 +198,7 @@ public class MenuBarv2 : Menuv2, IDesignable
{
if (sender is MenuBarItemv2 mbi)
{
if (e.CurrentValue)
if (e.Value)
{
Active = true;
}
@@ -541,7 +541,7 @@ public class MenuBarv2 : Menuv2, IDesignable
{
Normal = new (
GetAttributeForRole (VisualRole.Normal).Foreground,
args.CurrentValue,
args.Result,
GetAttributeForRole (VisualRole.Normal).Style)
});
};

View File

@@ -179,7 +179,7 @@ public class OptionSelector : View, IOrientation, IDesignable
Title = nameWithHotKey,
Id = name,
Data = index,
//HighlightStyle = HighlightStyle.Hover,
//HighlightStates = HighlightStates.Hover,
RadioStyle = true
};
@@ -193,37 +193,37 @@ public class OptionSelector : View, IOrientation, IDesignable
switch (e.Role)
{
case VisualRole.Normal:
e.Cancel = true;
e.Handled = true;
if (!HasFocus)
{
e.NewValue = GetAttributeForRole (VisualRole.Focus);
e.Result = GetAttributeForRole (VisualRole.Focus);
}
else
{
// If _scheme was set, it's because of Hover
if (checkbox.HasScheme)
{
e.NewValue = checkbox.GetAttributeForRole(VisualRole.Normal);
e.Result = checkbox.GetAttributeForRole(VisualRole.Normal);
}
else
{
e.NewValue = GetAttributeForRole (VisualRole.Normal);
e.Result = GetAttributeForRole (VisualRole.Normal);
}
}
break;
case VisualRole.HotNormal:
e.Cancel = true;
e.Handled = true;
if (!HasFocus)
{
e.NewValue = GetAttributeForRole (VisualRole.HotFocus);
e.Result = GetAttributeForRole (VisualRole.HotFocus);
}
else
{
e.NewValue = GetAttributeForRole (VisualRole.HotNormal);
e.Result = GetAttributeForRole (VisualRole.HotNormal);
}
break;

View File

@@ -434,7 +434,7 @@ public class ScrollBar : View, IOrientation, IDesignable
return;
}
RaiseSliderPositionChangeEvents (_sliderPosition, e.CurrentValue);
RaiseSliderPositionChangeEvents (_sliderPosition, e.Value);
}
private void SliderOnScroll (object? sender, EventArgs<int> e)
@@ -446,14 +446,14 @@ public class ScrollBar : View, IOrientation, IDesignable
int calculatedSliderPos = CalculateSliderPositionFromContentPosition (
_position,
e.CurrentValue >= 0 ? NavigationDirection.Forward : NavigationDirection.Backward);
e.Value >= 0 ? NavigationDirection.Forward : NavigationDirection.Backward);
if (calculatedSliderPos == _sliderPosition)
{
return;
}
int sliderScrolledAmount = e.CurrentValue;
int sliderScrolledAmount = e.Value;
int calculatedPosition = CalculatePositionFromSliderPosition (calculatedSliderPos + sliderScrolledAmount);
Position = calculatedPosition;
@@ -610,7 +610,7 @@ public class ScrollBar : View, IOrientation, IDesignable
{
OrientationChanged += (sender, args) =>
{
if (args.CurrentValue == Orientation.Vertical)
if (args.Value == Orientation.Vertical)
{
Width = 1;
Height = Dim.Fill ();

View File

@@ -26,7 +26,7 @@ public class ScrollSlider : View, IOrientation, IDesignable
OnOrientationChanged (Orientation);
HighlightStyle = HighlightStyle.Hover;
HighlightStates = ViewBase.MouseState.In;
}
#region IOrientation members

View File

@@ -60,7 +60,7 @@ public class Shortcut : View, IOrientation, IDesignable
/// <param name="helpText">The help text to display.</param>
public Shortcut (Key key, string? commandText, Action? action, string? helpText = null)
{
HighlightStyle = HighlightStyle.None;
HighlightStates = ViewBase.MouseState.None;
CanFocus = true;
if (Border is { })
@@ -481,7 +481,7 @@ public class Shortcut : View, IOrientation, IDesignable
CommandView.VerticalTextAlignment = Alignment.Center;
CommandView.TextAlignment = Alignment.Start;
CommandView.TextFormatter.WordWrap = false;
//CommandView.HighlightStyle = HighlightStyle.None;
//CommandView.HighlightStates = HighlightStates.None;
CommandView.GettingAttributeForRole += SubViewOnGettingAttributeForRole;
}
@@ -492,16 +492,16 @@ public class Shortcut : View, IOrientation, IDesignable
case VisualRole.Normal:
if (HasFocus)
{
e.Cancel = true;
e.NewValue = GetAttributeForRole (VisualRole.Focus);
e.Handled = true;
e.Result = GetAttributeForRole (VisualRole.Focus);
}
break;
case VisualRole.HotNormal:
if (HasFocus)
{
e.Cancel = true;
e.NewValue = GetAttributeForRole (VisualRole.HotFocus);
e.Handled = true;
e.Result = GetAttributeForRole (VisualRole.HotFocus);
}
break;
}
@@ -547,7 +547,7 @@ public class Shortcut : View, IOrientation, IDesignable
HelpView.VerticalTextAlignment = Alignment.Center;
HelpView.TextAlignment = Alignment.Start;
HelpView.TextFormatter.WordWrap = false;
HelpView.HighlightStyle = HighlightStyle.None;
HelpView.HighlightStates = ViewBase.MouseState.None;
HelpView.GettingAttributeForRole += SubViewOnGettingAttributeForRole;
}
@@ -681,14 +681,14 @@ public class Shortcut : View, IOrientation, IDesignable
KeyView.TextAlignment = Alignment.End;
KeyView.VerticalTextAlignment = Alignment.Center;
KeyView.KeyBindings.Clear ();
KeyView.HighlightStyle = HighlightStyle.None;
KeyView.HighlightStates = ViewBase.MouseState.None;
KeyView.GettingAttributeForRole += (sender, args) =>
{
if (args.Role == VisualRole.Normal)
{
args.NewValue = SuperView?.GetAttributeForRole (HasFocus ? VisualRole.HotFocus : VisualRole.HotNormal) ?? Attribute.Default;
args.Cancel = true;
args.Result = SuperView?.GetAttributeForRole (HasFocus ? VisualRole.HotFocus : VisualRole.HotNormal) ?? Attribute.Default;
args.Handled = true;
}
};
KeyView.ClearingViewport += (sender, args) =>

View File

@@ -184,15 +184,15 @@ public class DateField : TextField
}
}
private void DateField_Changing (object sender, CancelEventArgs<string> e)
private void OnTextChanging (object sender, ResultEventArgs<string> e)
{
try
{
var spaces = 0;
for (var i = 0; i < e.NewValue.Length; i++)
for (var i = 0; i < e.Result.Length; i++)
{
if (e.NewValue [i] == ' ')
if (e.Result [i] == ' ')
{
spaces++;
}
@@ -203,21 +203,22 @@ public class DateField : TextField
}
spaces += FormatLength;
string trimmedText = e.NewValue [..spaces];
string trimmedText = e.Result [..spaces];
spaces -= FormatLength;
trimmedText = trimmedText.Replace (new string (' ', spaces), " ");
var date = Convert.ToDateTime (trimmedText).ToString (_format.Trim ());
if ($" {date}" != e.NewValue)
if ($" {date}" != e.Result)
{
e.NewValue = $" {date}".Replace (RightToLeftMark, "");
// Change the date format to match the current culture
e.Result = $" {date}".Replace (RightToLeftMark, "");
}
AdjCursorPosition (CursorPosition);
}
catch (Exception)
{
e.Cancel = true;
e.Handled = true;
}
}
@@ -375,7 +376,7 @@ public class DateField : TextField
_separator = GetDataSeparator (Culture.DateTimeFormat.DateSeparator);
Date = date;
CursorPosition = 1;
TextChanging += DateField_Changing;
TextChanging += OnTextChanging;
// Things this view knows how to do
AddCommand (

View File

@@ -518,10 +518,10 @@ public class TextField : View, IDesignable
}
string newText = value.Replace ("\t", "").Split ("\n") [0];
CancelEventArgs<string> args = new (ref oldText, ref newText);
OnTextChanging (args);
ResultEventArgs<string> args = new (newText);
RaiseTextChanging (args);
if (args.Cancel)
if (args.Handled)
{
if (_cursorPosition > _text.Count)
{
@@ -534,7 +534,7 @@ public class TextField : View, IDesignable
ClearAllSelection ();
// Note we use NewValue here; TextChanging subscribers may have changed it
_text = args.NewValue.EnumerateRunes ().ToList ();
_text = args.Result.EnumerateRunes ().ToList ();
if (!Secret && !_historyText.IsFromHistory)
{
@@ -1067,16 +1067,21 @@ public class TextField : View, IDesignable
return true;
}
/// <summary>Virtual method that invoke the <see cref="TextChanging"/> event if it's defined.</summary>
/// <summary>Raises the <see cref="TextChanging"/> event, enabling canceling the change or adjusting the text.</summary>
/// <param name="args">The event arguments.</param>
/// <returns><see langword="true"/> if the event was cancelled.</returns>
public bool OnTextChanging (CancelEventArgs<string> args)
/// <returns><see langword="true"/> if the event was cancelled or the text was adjusted by the event.</returns>
public bool RaiseTextChanging (ResultEventArgs<string> args)
{
// TODO: CWP: Add an OnTextChanging protected virtual method that can be overridden to handle text changing events.
TextChanging?.Invoke (this, args);
return args.Cancel;
return args.Handled;
}
/// <summary>Raised before <see cref="Text"/> changes. The change can be canceled the text adjusted.</summary>
public event EventHandler<ResultEventArgs<string>> TextChanging;
/// <summary>Paste the selected text from the clipboard.</summary>
public virtual void Paste ()
{
@@ -1167,8 +1172,6 @@ public class TextField : View, IDesignable
///// </summary>
//public event EventHandler<StateEventArgs<string>> TextChanged;
/// <summary>Changing event, raised before the <see cref="Text"/> changes and can be canceled or changing the new text.</summary>
public event EventHandler<CancelEventArgs<string>> TextChanging;
/// <summary>Undoes the latest changes.</summary>
public void Undo ()

View File

@@ -432,15 +432,15 @@ public class TimeField : TextField
return true;
}
private void TextField_TextChanging (object sender, CancelEventArgs<string> e)
private void TextField_TextChanging (object sender, ResultEventArgs<string> e)
{
try
{
var spaces = 0;
for (var i = 0; i < e.NewValue.Length; i++)
for (var i = 0; i < e.Result.Length; i++)
{
if (e.NewValue [i] == ' ')
if (e.Result [i] == ' ')
{
spaces++;
}
@@ -451,31 +451,31 @@ public class TimeField : TextField
}
spaces += FieldLength;
string trimmedText = e.NewValue [..spaces];
string trimmedText = e.Result [..spaces];
spaces -= FieldLength;
trimmedText = trimmedText.Replace (new string (' ', spaces), " ");
if (trimmedText != e.NewValue)
if (trimmedText != e.Result)
{
e.NewValue = trimmedText;
e.Result = trimmedText;
}
if (!TimeSpan.TryParseExact (
e.NewValue.Trim (),
e.Result.Trim (),
Format.Trim (),
CultureInfo.CurrentCulture,
TimeSpanStyles.None,
out TimeSpan result
))
{
e.Cancel = true;
e.Handled = true;
}
AdjCursorPosition (CursorPosition);
}
catch (Exception)
{
e.Cancel = true;
e.Handled = true;
}
}
}

View File

@@ -0,0 +1 @@

View File

@@ -563,7 +563,7 @@ public class Wizard : Dialog
{
if (string.IsNullOrEmpty (_wizardTitle))
{
_wizardTitle = e.CurrentValue;
_wizardTitle = e.Value;
}
}
}

View File

@@ -381,6 +381,7 @@
<s:Boolean x:Key="/Default/CodeStyle/EditorConfig/EnableEditorConfigSupport/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/EditorConfig/ShowEditorConfigStatusBarIndicator/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/EditorConfig/SyncToVisualStudio/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CWP/@EntryIndexedValue">CWP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LL/@EntryIndexedValue">LL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LR/@EntryIndexedValue">LR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
@@ -411,6 +412,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Int64 x:Key="/Default/Environment/UnitTesting/ParallelProcessesCount/@EntryValue">5</s:Int64>
<s:Boolean x:Key="/Default/GrammarAndSpelling/GrammarChecking/Exceptions/=Attribute_0020attribute/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Gainsboro/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Gonek/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Guppie/@EntryIndexedValue">True</s:Boolean>

View File

@@ -87,7 +87,7 @@ public class ScenarioTests : TestsAllViews
void OnApplicationOnInitializedChanged (object? s, EventArgs<bool> a)
{
if (a.CurrentValue)
if (a.Value)
{
Application.Iteration += OnApplicationOnIteration;
initialized = true;
@@ -103,7 +103,7 @@ public class ScenarioTests : TestsAllViews
shutdownGracefully = true;
}
_output.WriteLine ($"Initialized == {a.CurrentValue}; shutdownGracefully == {shutdownGracefully}.");
_output.WriteLine ($"Initialized == {a.Value}; shutdownGracefully == {shutdownGracefully}.");
}
// If the scenario doesn't close within abortTime ms, this will force it to quit

View File

@@ -90,7 +90,7 @@ public class ScenariosStressTests : TestsAllViews
void OnApplicationOnInitializedChanged (object? s, EventArgs<bool> a)
{
if (a.CurrentValue)
if (a.Value)
{
lock (_timeoutLock)
{
@@ -106,7 +106,7 @@ public class ScenariosStressTests : TestsAllViews
{
refreshedCount++;
if (args.CurrentValue)
if (args.Value)
{
updatedCount++;
}
@@ -124,7 +124,7 @@ public class ScenariosStressTests : TestsAllViews
stopwatch!.Stop ();
}
_output.WriteLine ($"Initialized == {a.CurrentValue}");
_output.WriteLine ($"Initialized == {a.Value}");
}
void OnApplicationOnIteration (object? s, IterationEventArgs a)

Some files were not shown because too many files have changed in this diff Show More