mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Merge pull request #2299 from tznind/tabviewclick
Fixes #2298 - Adds TabView.TabClicked event
This commit is contained in:
@@ -53,6 +53,14 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
public event EventHandler<TabChangedEventArgs> SelectedTabChanged;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when a <see cref="TabView.Tab"/> is clicked. Can be used to cancel navigation,
|
||||
/// show context menu (e.g. on right click) etc.
|
||||
/// </summary>
|
||||
public event EventHandler<TabMouseEventArgs> TabClicked;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected member of <see cref="Tabs"/> chosen by the user
|
||||
/// </summary>
|
||||
@@ -665,6 +673,22 @@ namespace Terminal.Gui {
|
||||
|
||||
public override bool MouseEvent (MouseEvent me)
|
||||
{
|
||||
var hit = ScreenToTab (me.X, me.Y);
|
||||
|
||||
bool isClick = me.Flags.HasFlag (MouseFlags.Button1Clicked) ||
|
||||
me.Flags.HasFlag (MouseFlags.Button2Clicked) ||
|
||||
me.Flags.HasFlag (MouseFlags.Button3Clicked);
|
||||
|
||||
if (isClick) {
|
||||
host.OnTabClicked (new TabMouseEventArgs (hit, me));
|
||||
|
||||
// user canceled click
|
||||
if (me.Handled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) &&
|
||||
!me.Flags.HasFlag (MouseFlags.Button1DoubleClicked) &&
|
||||
!me.Flags.HasFlag (MouseFlags.Button1TripleClicked))
|
||||
@@ -689,7 +713,7 @@ namespace Terminal.Gui {
|
||||
return true;
|
||||
}
|
||||
|
||||
var hit = ScreenToTab (me.X, me.Y);
|
||||
|
||||
if (hit != null) {
|
||||
host.SelectedTab = hit;
|
||||
SetNeedsDisplay ();
|
||||
@@ -738,6 +762,45 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="TabClicked"/> event.
|
||||
/// </summary>
|
||||
/// <param name="tabMouseEventArgs"></param>
|
||||
protected virtual private void OnTabClicked (TabMouseEventArgs tabMouseEventArgs)
|
||||
{
|
||||
TabClicked?.Invoke (this, tabMouseEventArgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a mouse event over a specific <see cref="TabView.Tab"/> in a <see cref="TabView"/>.
|
||||
/// </summary>
|
||||
public class TabMouseEventArgs : EventArgs {
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="TabView.Tab"/> (if any) that the mouse
|
||||
/// was over when the <see cref="MouseEvent"/> occurred.
|
||||
/// </summary>
|
||||
/// <remarks>This will be null if the click is after last tab
|
||||
/// or before first.</remarks>
|
||||
public Tab Tab { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual mouse event. Use <see cref="MouseEvent.Handled"/> to cancel this event
|
||||
/// and perform custom behavior (e.g. show a context menu).
|
||||
/// </summary>
|
||||
public MouseEvent MouseEvent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="TabMouseEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tab"><see cref="TabView.Tab"/> that the mouse was over when the event occurred.</param>
|
||||
/// <param name="mouseEvent">The mouse activity being reported</param>
|
||||
public TabMouseEventArgs (Tab tab, MouseEvent mouseEvent)
|
||||
{
|
||||
Tab = tab;
|
||||
MouseEvent = mouseEvent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A single tab in a <see cref="TabView"/>
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace UICatalog.Scenarios {
|
||||
Height = Dim.Fill (1),
|
||||
};
|
||||
|
||||
tabView.TabClicked += TabView_TabClicked;
|
||||
|
||||
tabView.Style.ShowBorder = true;
|
||||
tabView.ApplyStyleChanges ();
|
||||
|
||||
@@ -63,6 +65,34 @@ namespace UICatalog.Scenarios {
|
||||
New ();
|
||||
}
|
||||
|
||||
private void TabView_TabClicked (object sender, TabView.TabMouseEventArgs e)
|
||||
{
|
||||
// we are only interested in right clicks
|
||||
if(!e.MouseEvent.Flags.HasFlag(MouseFlags.Button3Clicked)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MenuBarItem items;
|
||||
|
||||
if (e.Tab == null) {
|
||||
items = new MenuBarItem (new MenuItem [] {
|
||||
new MenuItem ($"Open", "", () => Open()),
|
||||
});
|
||||
|
||||
} else {
|
||||
items = new MenuBarItem (new MenuItem [] {
|
||||
new MenuItem ($"Save", "", () => Save(e.Tab)),
|
||||
new MenuItem ($"Close", "", () => Close(e.Tab)),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var contextMenu = new ContextMenu (e.MouseEvent.X + 1, e.MouseEvent.Y + 1, items);
|
||||
|
||||
contextMenu.Show ();
|
||||
e.MouseEvent.Handled = true;
|
||||
}
|
||||
|
||||
private void New ()
|
||||
{
|
||||
Open ("", null, $"new {numbeOfNewTabs++}");
|
||||
@@ -70,7 +100,11 @@ namespace UICatalog.Scenarios {
|
||||
|
||||
private void Close ()
|
||||
{
|
||||
var tab = tabView.SelectedTab as OpenedFile;
|
||||
Close (tabView.SelectedTab);
|
||||
}
|
||||
private void Close (TabView.Tab tabToClose)
|
||||
{
|
||||
var tab = tabToClose as OpenedFile;
|
||||
|
||||
if (tab == null) {
|
||||
return;
|
||||
@@ -158,7 +192,11 @@ namespace UICatalog.Scenarios {
|
||||
|
||||
public void Save ()
|
||||
{
|
||||
var tab = tabView.SelectedTab as OpenedFile;
|
||||
Save (tabView.SelectedTab);
|
||||
}
|
||||
public void Save (TabView.Tab tabToSave)
|
||||
{
|
||||
var tab = tabToSave as OpenedFile;
|
||||
|
||||
if (tab == null) {
|
||||
return;
|
||||
|
||||
@@ -760,6 +760,98 @@ namespace Terminal.Gui.ViewTests {
|
||||
└──────────────┘ ", output);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
public void MouseClick_ChangesTab ()
|
||||
{
|
||||
var tv = GetTabView (out var tab1, out var tab2, false);
|
||||
|
||||
tv.Width = 20;
|
||||
tv.Height = 5;
|
||||
|
||||
tv.LayoutSubviews ();
|
||||
|
||||
tv.Redraw (tv.Bounds);
|
||||
|
||||
var tabRow = tv.Subviews[0];
|
||||
Assert.Equal("TabRowView",tabRow.GetType().Name);
|
||||
|
||||
TestHelpers.AssertDriverContentsAre (@"
|
||||
┌────┐
|
||||
│Tab1│Tab2
|
||||
│ └─────────────┐
|
||||
│hi │
|
||||
└──────────────────┘
|
||||
", output);
|
||||
|
||||
TabView.Tab clicked = null;
|
||||
|
||||
|
||||
tv.TabClicked += (s,e)=>{
|
||||
clicked = e.Tab;
|
||||
};
|
||||
|
||||
// Waving mouse around does not trigger click
|
||||
for(int i=0;i<100;i++)
|
||||
{
|
||||
tabRow.MouseEvent(new MouseEvent{
|
||||
X = i,
|
||||
Y = 1,
|
||||
Flags = MouseFlags.ReportMousePosition
|
||||
});
|
||||
|
||||
Assert.Null(clicked);
|
||||
Assert.Equal(tab1, tv.SelectedTab);
|
||||
}
|
||||
|
||||
tabRow.MouseEvent(new MouseEvent{
|
||||
X = 3,
|
||||
Y = 1,
|
||||
Flags = MouseFlags.Button1Clicked
|
||||
});
|
||||
|
||||
Assert.Equal(tab1, clicked);
|
||||
Assert.Equal(tab1, tv.SelectedTab);
|
||||
|
||||
|
||||
// Click to tab2
|
||||
tabRow.MouseEvent(new MouseEvent{
|
||||
X = 7,
|
||||
Y = 1,
|
||||
Flags = MouseFlags.Button1Clicked
|
||||
});
|
||||
|
||||
Assert.Equal(tab2, clicked);
|
||||
Assert.Equal(tab2, tv.SelectedTab);
|
||||
|
||||
// cancel navigation
|
||||
tv.TabClicked += (s,e)=>{
|
||||
clicked = e.Tab;
|
||||
e.MouseEvent.Handled = true;
|
||||
};
|
||||
|
||||
tabRow.MouseEvent(new MouseEvent{
|
||||
X = 3,
|
||||
Y = 1,
|
||||
Flags = MouseFlags.Button1Clicked
|
||||
});
|
||||
|
||||
// Tab 1 was clicked but event handler blocked navigation
|
||||
Assert.Equal(tab1, clicked);
|
||||
Assert.Equal(tab2, tv.SelectedTab);
|
||||
|
||||
|
||||
tabRow.MouseEvent (new MouseEvent {
|
||||
X = 10,
|
||||
Y = 1,
|
||||
Flags = MouseFlags.Button1Clicked
|
||||
});
|
||||
|
||||
// Clicking beyond last tab should raise event with null Tab
|
||||
Assert.Null (clicked);
|
||||
Assert.Equal (tab2, tv.SelectedTab);
|
||||
|
||||
}
|
||||
|
||||
private void InitFakeDriver ()
|
||||
{
|
||||
var driver = new FakeDriver ();
|
||||
|
||||
Reference in New Issue
Block a user