using System.ComponentModel;
using System.Diagnostics;
namespace Terminal.Gui;
public partial class View
{
///
/// Gets or sets whether the will be highlighted visually while the mouse button is
/// pressed.
///
public bool HighlightOnPress { get; set; }
/// Gets or sets whether the wants continuous button pressed events.
public virtual bool WantContinuousButtonPressed { get; set; }
/// Gets or sets whether the wants mouse position reports.
/// if mouse position reports are wanted; otherwise, .
public virtual bool WantMousePositionReports { get; set; }
///
/// Called when the mouse enters the View's . The view will now receive mouse events until the
/// mouse leaves
/// the view. At which time, will be called.
///
///
/// The coordinates are relative to .
///
///
/// , if the event was handled, otherwise.
protected internal virtual bool OnMouseEnter (MouseEvent mouseEvent)
{
if (!Enabled)
{
return true;
}
if (!CanBeVisible (this))
{
return false;
}
var args = new MouseEventEventArgs (mouseEvent);
MouseEnter?.Invoke (this, args);
return args.Handled;
}
/// Event fired when the mouse moves into the View's .
public event EventHandler MouseEnter;
///
/// Called when the mouse has moved out of the View's . The view will no longer receive mouse
/// events (until the
/// mouse moves within the view again and is called).
///
///
/// The coordinates are relative to .
///
///
/// , if the event was handled, otherwise.
protected internal virtual bool OnMouseLeave (MouseEvent mouseEvent)
{
if (!Enabled)
{
return true;
}
if (!CanBeVisible (this))
{
return false;
}
var args = new MouseEventEventArgs (mouseEvent);
MouseLeave?.Invoke (this, args);
return args.Handled;
}
/// Event fired when the mouse leaves the View's .
public event EventHandler MouseLeave;
///
/// Processes a . This method is called by when a mouse
/// event occurs.
///
///
///
/// A view must be both enabled and visible to receive mouse events.
///
///
/// This method calls to process the event. If the event is not handled, and one of the
/// mouse buttons was clicked, it calls to process the click.
///
///
/// If is , the view will be highlighted when the mouse is
/// pressed.
/// See and for more information.
///
///
/// If is , the event
/// will be invoked repeatedly while the button is pressed.
///
///
///
/// if the event was handled, otherwise.
public bool? NewMouseEvent (MouseEvent mouseEvent)
{
if (!Enabled)
{
// A disabled view should not eat mouse events
return false;
}
if (!CanBeVisible (this))
{
return false;
}
if (OnMouseEvent (mouseEvent))
{
// Technically mouseEvent.Handled should already be true if implementers of OnMouseEvent
// follow the rules. But we'll update it just in case.
return mouseEvent.Handled = true;
}
if ((HighlightOnPress || WantContinuousButtonPressed) && Highlight (mouseEvent))
{
Debug.Assert (mouseEvent.Handled);
return mouseEvent.Handled;
}
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button2Clicked)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button4Clicked))
{
return OnMouseClick (new (mouseEvent));
}
return false;
}
///
/// Highlight the view when the mouse is pressed.
///
///
///
/// Set to to have the view highlighted when the mouse is
/// pressed.
///
///
/// Calls which fires the event.
///
///
/// Calls which fires the event.
///
///
///
/// , if the event was handled, otherwise.
private bool Highlight (MouseEvent mouseEvent)
{
if (Application.MouseGrabView == this
&& (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button2Clicked)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button4Clicked)))
{
// We're grabbed. Clicked event comes after the last Release. This is our signal to ungrab
Application.UngrabMouse ();
DisableHighlight ();
// If mouse is still in bounds, click
if (!WantContinuousButtonPressed && Bounds.Contains (mouseEvent.X, mouseEvent.Y))
{
return OnMouseClick (new (mouseEvent));
}
return mouseEvent.Handled = true;
}
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button2Pressed)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button3Pressed)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button4Pressed))
{
// The first time we get pressed event, grab the mouse and set focus
if (Application.MouseGrabView != this)
{
Application.GrabMouse (this);
if (CanFocus)
{
// Set the focus, but don't invoke Accept
SetFocus ();
}
}
if (Bounds.Contains (mouseEvent.X, mouseEvent.Y))
{
EnableHighlight ();
}
else
{
DisableHighlight ();
}
if (WantContinuousButtonPressed && Application.MouseGrabView == this)
{
// If this is not the first pressed event, click
return OnMouseClick (new (mouseEvent));
}
return mouseEvent.Handled = true;
}
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button2Released)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button3Released)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button4Released))
{
if (Application.MouseGrabView == this)
{
DisableHighlight ();
}
return mouseEvent.Handled = true;
}
return mouseEvent.Handled;
}
[CanBeNull]
private ColorScheme _savedHighlightColorScheme;
///
/// Enables the highlight for the view. Called from OnMouseEvent.
///
public void EnableHighlight ()
{
if (OnEnablingHighlight () == true)
{
return;
}
if (_savedHighlightColorScheme is null && ColorScheme is { })
{
_savedHighlightColorScheme ??= ColorScheme;
if (CanFocus)
{
// TODO: Make the inverted color configurable
var cs = new ColorScheme (ColorScheme)
{
// For Buttons etc...
Focus = new (ColorScheme.Normal.Foreground, ColorScheme.Focus.Background),
// For Adornments
Normal = new (ColorScheme.Focus.Foreground, ColorScheme.Normal.Background)
};
ColorScheme = cs;
}
else
{
var cs = new ColorScheme (ColorScheme)
{
// For Buttons etc... that can't focus (like up/down).
Normal = new (ColorScheme.Focus.Background, ColorScheme.Normal.Foreground)
};
ColorScheme = cs;
}
}
}
///
/// Fired when the view is highlighted. Set to
/// to implement a custom highlight scheme or prevent the view from being highlighted.
///
public event EventHandler EnablingHighlight;
///
/// Called when the view is to be highlighted.
///
/// , if the event was handled, otherwise.
protected virtual bool? OnEnablingHighlight ()
{
CancelEventArgs args = new ();
EnablingHighlight?.Invoke (this, args);
return args.Cancel;
}
///
/// Disables the highlight for the view. Called from OnMouseEvent.
///
public void DisableHighlight ()
{
if (OnDisablingHighlight () == true)
{
return;
}
// Unhighlight
if (_savedHighlightColorScheme is { })
{
ColorScheme = _savedHighlightColorScheme;
_savedHighlightColorScheme = null;
}
}
///
/// Fired when the view is no longer to be highlighted. Set to
///
/// to implement a custom highlight scheme or prevent the view from being highlighted.
///
public event EventHandler DisablingHighlight;
///
/// Called when the view is no longer to be highlighted.
///
/// , if the event was handled, otherwise.
protected virtual bool? OnDisablingHighlight ()
{
CancelEventArgs args = new ();
DisablingHighlight?.Invoke (this, args);
return args.Cancel;
}
/// Called when a mouse event occurs within the view's .
///
///
/// The coordinates are relative to .
///
///
///
/// , if the event was handled, otherwise.
protected internal virtual bool OnMouseEvent (MouseEvent mouseEvent)
{
var args = new MouseEventEventArgs (mouseEvent);
MouseEvent?.Invoke (this, args);
return args.Handled;
}
/// Event fired when a mouse event occurs.
///
///
/// The coordinates are relative to .
///
///
public event EventHandler MouseEvent;
/// Invokes the MouseClick event.
///
///
/// Called when the mouse is either clicked or double-clicked. Check
/// to see which button was clicked.
///
///
/// , if the event was handled, otherwise.
protected bool OnMouseClick (MouseEventEventArgs args)
{
if (!Enabled)
{
// QUESTION: Is this right? Should a disabled view eat mouse clicks?
args.Handled = true;
return true;
}
MouseClick?.Invoke (this, args);
if (args.Handled)
{
return true;
}
if (!HasFocus && CanFocus)
{
args.Handled = true;
SetFocus ();
}
return args.Handled;
}
/// Event fired when a mouse click occurs.
///
///
/// Fired when the mouse is either clicked or double-clicked. Check
/// to see which button was clicked.
///
///
/// The coordinates are relative to .
///
///
public event EventHandler MouseClick;
}