2024-06-26 05:56:29 -07:00
|
|
|
using System;
|
|
|
|
|
|
|
|
using Avalonia;
|
|
|
|
using Avalonia.Controls;
|
|
|
|
using Avalonia.Input;
|
|
|
|
using Avalonia.Interactivity;
|
2024-06-29 02:04:39 -07:00
|
|
|
using Avalonia.Media;
|
2024-06-26 05:56:29 -07:00
|
|
|
|
|
|
|
namespace SourceGit.Views
|
|
|
|
{
|
|
|
|
public partial class LauncherTabBar : UserControl
|
|
|
|
{
|
|
|
|
public LauncherTabBar()
|
|
|
|
{
|
|
|
|
InitializeComponent();
|
|
|
|
}
|
|
|
|
|
2024-06-29 02:04:39 -07:00
|
|
|
public override void Render(DrawingContext context)
|
|
|
|
{
|
|
|
|
base.Render(context);
|
|
|
|
|
|
|
|
if (LauncherTabsList == null || LauncherTabsList.SelectedIndex == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var startX = LauncherTabsScroller.Offset.X;
|
|
|
|
var endX = startX + LauncherTabsScroller.Viewport.Width;
|
|
|
|
var height = LauncherTabsScroller.Viewport.Height;
|
|
|
|
|
|
|
|
var selectedIdx = LauncherTabsList.SelectedIndex;
|
|
|
|
var count = LauncherTabsList.ItemCount;
|
|
|
|
var separatorPen = new Pen(this.FindResource("Brush.FG2") as IBrush, 0.5);
|
|
|
|
var separatorY = (height - 20) * 0.5;
|
|
|
|
for (var i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
if (i == selectedIdx || i == selectedIdx - 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
var container = LauncherTabsList.ContainerFromIndex(i);
|
2024-07-14 00:55:15 -07:00
|
|
|
if (container == null)
|
|
|
|
continue;
|
|
|
|
|
2024-06-29 02:04:39 -07:00
|
|
|
var containerEndX = container.Bounds.Right;
|
|
|
|
if (containerEndX < startX || containerEndX > endX)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
var separatorX = containerEndX - startX + LauncherTabsScroller.Bounds.X;
|
|
|
|
context.DrawLine(separatorPen, new Point(separatorX, separatorY), new Point(separatorX, separatorY + 20));
|
|
|
|
}
|
|
|
|
|
|
|
|
var selected = LauncherTabsList.ContainerFromIndex(selectedIdx);
|
2024-07-14 00:55:15 -07:00
|
|
|
if (selected == null)
|
|
|
|
return;
|
|
|
|
|
2024-06-29 02:04:39 -07:00
|
|
|
var activeStartX = selected.Bounds.X;
|
|
|
|
var activeEndX = activeStartX + selected.Bounds.Width;
|
2024-06-30 04:03:23 -07:00
|
|
|
if (activeStartX > endX + 5 || activeEndX < startX - 5)
|
2024-06-29 02:04:39 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
var geo = new StreamGeometry();
|
|
|
|
var angle = Math.PI / 2;
|
2024-06-30 04:03:23 -07:00
|
|
|
var y = height + 0.25;
|
2024-06-29 02:04:39 -07:00
|
|
|
using (var ctx = geo.Open())
|
|
|
|
{
|
2024-07-14 00:55:15 -07:00
|
|
|
double x;
|
|
|
|
|
2024-06-29 02:04:39 -07:00
|
|
|
var drawLeftX = activeStartX - startX + LauncherTabsScroller.Bounds.X;
|
|
|
|
var drawRightX = activeEndX - startX + LauncherTabsScroller.Bounds.X;
|
|
|
|
if (drawLeftX < LauncherTabsScroller.Bounds.X)
|
|
|
|
{
|
|
|
|
x = LauncherTabsScroller.Bounds.X;
|
|
|
|
ctx.BeginFigure(new Point(x, y), true);
|
2024-06-29 02:15:10 -07:00
|
|
|
y = 1;
|
2024-06-29 02:04:39 -07:00
|
|
|
ctx.LineTo(new Point(x, y));
|
|
|
|
x = drawRightX - 6;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-06-30 04:03:23 -07:00
|
|
|
x = drawLeftX - 5;
|
2024-06-29 02:04:39 -07:00
|
|
|
ctx.BeginFigure(new Point(x, y), true);
|
|
|
|
x = drawLeftX;
|
2024-06-30 04:03:23 -07:00
|
|
|
y -= 5;
|
|
|
|
ctx.ArcTo(new Point(x, y), new Size(5, 5), angle, false, SweepDirection.CounterClockwise);
|
2024-06-29 02:04:39 -07:00
|
|
|
y = 6;
|
|
|
|
ctx.LineTo(new Point(x, y));
|
|
|
|
x += 6;
|
2024-06-29 02:15:10 -07:00
|
|
|
y = 1;
|
2024-06-29 02:04:39 -07:00
|
|
|
ctx.ArcTo(new Point(x, y), new Size(6, 6), angle, false, SweepDirection.Clockwise);
|
|
|
|
x = drawRightX - 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (drawRightX < LauncherTabsScroller.Bounds.Right)
|
|
|
|
{
|
|
|
|
ctx.LineTo(new Point(x, y));
|
|
|
|
x = drawRightX;
|
|
|
|
y = 6;
|
|
|
|
ctx.ArcTo(new Point(x, y), new Size(6, 6), angle, false, SweepDirection.Clockwise);
|
2024-06-30 04:03:23 -07:00
|
|
|
y = height + 0.25 - 5;
|
2024-06-29 02:04:39 -07:00
|
|
|
ctx.LineTo(new Point(x, y));
|
2024-06-30 04:03:23 -07:00
|
|
|
x += 5;
|
|
|
|
y = height + 0.25;
|
|
|
|
ctx.ArcTo(new Point(x, y), new Size(5, 5), angle, false, SweepDirection.CounterClockwise);
|
2024-06-29 02:04:39 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x = LauncherTabsScroller.Bounds.Right;
|
|
|
|
ctx.LineTo(new Point(x, y));
|
2024-06-30 04:03:23 -07:00
|
|
|
y = height + 0.25;
|
2024-06-29 02:04:39 -07:00
|
|
|
ctx.LineTo(new Point(x, y));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var fill = this.FindResource("Brush.ToolBar") as IBrush;
|
2024-07-14 00:55:15 -07:00
|
|
|
var stroke = new Pen(this.FindResource("Brush.Border0") as IBrush);
|
2024-06-29 02:04:39 -07:00
|
|
|
context.DrawGeometry(fill, stroke, geo);
|
|
|
|
}
|
|
|
|
|
2024-07-14 00:55:15 -07:00
|
|
|
private void ScrollTabs(object _, PointerWheelEventArgs e)
|
2024-06-26 05:56:29 -07:00
|
|
|
{
|
|
|
|
if (!e.KeyModifiers.HasFlag(KeyModifiers.Shift))
|
|
|
|
{
|
|
|
|
if (e.Delta.Y < 0)
|
|
|
|
LauncherTabsScroller.LineRight();
|
|
|
|
else if (e.Delta.Y > 0)
|
|
|
|
LauncherTabsScroller.LineLeft();
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-14 00:55:15 -07:00
|
|
|
private void ScrollTabsLeft(object _, RoutedEventArgs e)
|
2024-06-26 05:56:29 -07:00
|
|
|
{
|
|
|
|
LauncherTabsScroller.LineLeft();
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
2024-07-14 00:55:15 -07:00
|
|
|
private void ScrollTabsRight(object _, RoutedEventArgs e)
|
2024-06-26 05:56:29 -07:00
|
|
|
{
|
|
|
|
LauncherTabsScroller.LineRight();
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
2024-07-14 00:55:15 -07:00
|
|
|
private void OnTabsLayoutUpdated(object _1, EventArgs _2)
|
2024-06-26 05:56:29 -07:00
|
|
|
{
|
|
|
|
if (LauncherTabsScroller.Extent.Width > LauncherTabsScroller.Viewport.Width)
|
|
|
|
{
|
|
|
|
LeftScrollIndicator.IsVisible = true;
|
|
|
|
LeftScrollIndicator.IsEnabled = LauncherTabsScroller.Offset.X > 0;
|
|
|
|
RightScrollIndicator.IsVisible = true;
|
|
|
|
RightScrollIndicator.IsEnabled = LauncherTabsScroller.Offset.X < LauncherTabsScroller.Extent.Width - LauncherTabsScroller.Viewport.Width;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LeftScrollIndicator.IsVisible = false;
|
|
|
|
RightScrollIndicator.IsVisible = false;
|
|
|
|
}
|
2024-06-29 02:04:39 -07:00
|
|
|
|
|
|
|
InvalidateVisual();
|
|
|
|
}
|
|
|
|
|
2024-07-14 00:55:15 -07:00
|
|
|
private void OnTabsSelectionChanged(object _1, SelectionChangedEventArgs _2)
|
2024-06-29 02:04:39 -07:00
|
|
|
{
|
|
|
|
InvalidateVisual();
|
2024-06-26 05:56:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private void SetupDragAndDrop(object sender, RoutedEventArgs e)
|
|
|
|
{
|
|
|
|
if (sender is Border border)
|
|
|
|
{
|
|
|
|
DragDrop.SetAllowDrop(border, true);
|
|
|
|
border.AddHandler(DragDrop.DropEvent, DropTab);
|
|
|
|
}
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnPointerPressedTab(object sender, PointerPressedEventArgs e)
|
|
|
|
{
|
2024-07-14 00:55:15 -07:00
|
|
|
if (sender is Border border)
|
2024-06-26 05:56:29 -07:00
|
|
|
{
|
2024-07-14 00:55:15 -07:00
|
|
|
var point = e.GetCurrentPoint(border);
|
|
|
|
if (point.Properties.IsMiddleButtonPressed && border.DataContext is ViewModels.LauncherPage page)
|
|
|
|
{
|
|
|
|
(DataContext as ViewModels.Launcher)?.CloseTab(page);
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_pressedTab = true;
|
|
|
|
_startDragTab = false;
|
|
|
|
_pressedTabPosition = e.GetPosition(border);
|
|
|
|
}
|
2024-06-26 05:56:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-14 00:55:15 -07:00
|
|
|
private void OnPointerReleasedTab(object _1, PointerReleasedEventArgs _2)
|
2024-06-26 05:56:29 -07:00
|
|
|
{
|
|
|
|
_pressedTab = false;
|
|
|
|
_startDragTab = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnPointerMovedOverTab(object sender, PointerEventArgs e)
|
|
|
|
{
|
2024-07-14 00:55:15 -07:00
|
|
|
if (_pressedTab && !_startDragTab && sender is Border { DataContext: ViewModels.LauncherPage page } border)
|
2024-06-26 05:56:29 -07:00
|
|
|
{
|
|
|
|
var delta = e.GetPosition(border) - _pressedTabPosition;
|
|
|
|
var sizeSquired = delta.X * delta.X + delta.Y * delta.Y;
|
|
|
|
if (sizeSquired < 64)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_startDragTab = true;
|
|
|
|
|
|
|
|
var data = new DataObject();
|
2024-07-14 00:55:15 -07:00
|
|
|
data.Set("MovedTab", page);
|
2024-06-26 05:56:29 -07:00
|
|
|
DragDrop.DoDragDrop(e, data, DragDropEffects.Move);
|
|
|
|
}
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void DropTab(object sender, DragEventArgs e)
|
|
|
|
{
|
2024-07-14 00:55:15 -07:00
|
|
|
if (e.Data.Get("MovedTab") is ViewModels.LauncherPage moved &&
|
|
|
|
sender is Border { DataContext: ViewModels.LauncherPage to } &&
|
|
|
|
to != moved)
|
2024-06-26 05:56:29 -07:00
|
|
|
{
|
2024-07-14 00:55:15 -07:00
|
|
|
(DataContext as ViewModels.Launcher)?.MoveTab(moved, to);
|
2024-06-26 05:56:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
_pressedTab = false;
|
|
|
|
_startDragTab = false;
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnTabContextRequested(object sender, ContextRequestedEventArgs e)
|
|
|
|
{
|
|
|
|
if (sender is Border border && DataContext is ViewModels.Launcher vm)
|
|
|
|
{
|
|
|
|
var menu = vm.CreateContextForPageTab(border.DataContext as ViewModels.LauncherPage);
|
|
|
|
border.OpenContextMenu(menu);
|
|
|
|
}
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnCloseTab(object sender, RoutedEventArgs e)
|
|
|
|
{
|
|
|
|
if (sender is Button btn && DataContext is ViewModels.Launcher vm)
|
|
|
|
vm.CloseTab(btn.DataContext as ViewModels.LauncherPage);
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool _pressedTab = false;
|
|
|
|
private Point _pressedTabPosition = new Point();
|
|
|
|
private bool _startDragTab = false;
|
|
|
|
}
|
|
|
|
}
|