mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-23 20:47:25 -08:00
refactor: custom renderer for launcher tab bar
This commit is contained in:
parent
bfea573d4b
commit
1241539260
6 changed files with 112 additions and 47 deletions
|
@ -627,10 +627,6 @@
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="Border.launcher_pagetab">
|
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
|
||||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
|
||||||
</Style>
|
|
||||||
<Style Selector="ListBox.launcher_page_tabbar">
|
<Style Selector="ListBox.launcher_page_tabbar">
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
@ -642,15 +638,13 @@
|
||||||
<Setter Property="Opacity" Value=".5"/>
|
<Setter Property="Opacity" Value=".5"/>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="ListBox.launcher_page_tabbar ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
<Style Selector="ListBox.launcher_page_tabbar ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
<Setter Property="Opacity" Value=".85"/>
|
<Setter Property="Opacity" Value=".85"/>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="ListBox.launcher_page_tabbar ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
|
<Style Selector="ListBox.launcher_page_tabbar ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
<Setter Property="Opacity" Value="1"/>
|
<Setter Property="Opacity" Value="1"/>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="ListBoxItem:selected Border.launcher_pagetab">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource Brush.ToolBar}"/>
|
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Border0}"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="ContextMenu">
|
<Style Selector="ContextMenu">
|
||||||
<Setter Property="HorizontalOffset" Value="-4"/>
|
<Setter Property="HorizontalOffset" Value="-4"/>
|
||||||
|
|
|
@ -24,10 +24,7 @@ namespace SourceGit.ViewModels
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _activePage, value))
|
if (SetProperty(ref _activePage, value))
|
||||||
{
|
|
||||||
PopupHost.Active = value;
|
PopupHost.Active = value;
|
||||||
UpdateTabSplitterVisible();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,9 +66,7 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
var lastActiveIdx = Preference.Instance.LastActiveTabIdx;
|
var lastActiveIdx = Preference.Instance.LastActiveTabIdx;
|
||||||
if (lastActiveIdx >= 0 && lastActiveIdx < Pages.Count)
|
if (lastActiveIdx >= 0 && lastActiveIdx < Pages.Count)
|
||||||
{
|
|
||||||
ActivePage = Pages[lastActiveIdx];
|
ActivePage = Pages[lastActiveIdx];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,13 +156,11 @@ namespace SourceGit.ViewModels
|
||||||
ActivePage = Pages[removeIdx == Pages.Count - 1 ? removeIdx - 1 : removeIdx + 1];
|
ActivePage = Pages[removeIdx == Pages.Count - 1 ? removeIdx - 1 : removeIdx + 1];
|
||||||
CloseRepositoryInTab(page);
|
CloseRepositoryInTab(page);
|
||||||
Pages.RemoveAt(removeIdx);
|
Pages.RemoveAt(removeIdx);
|
||||||
UpdateTabSplitterVisible();
|
|
||||||
}
|
}
|
||||||
else if (removeIdx + 1 == activeIdx)
|
else if (removeIdx + 1 == activeIdx)
|
||||||
{
|
{
|
||||||
CloseRepositoryInTab(page);
|
CloseRepositoryInTab(page);
|
||||||
Pages.RemoveAt(removeIdx);
|
Pages.RemoveAt(removeIdx);
|
||||||
UpdateTabSplitterVisible();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -365,13 +358,6 @@ namespace SourceGit.ViewModels
|
||||||
page.Data = null;
|
page.Data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateTabSplitterVisible()
|
|
||||||
{
|
|
||||||
var activePageIdx = ActivePage == null ? -1 : Pages.IndexOf(ActivePage);
|
|
||||||
for (int i = 0; i < Pages.Count; i++)
|
|
||||||
Pages[i].IsTabSplitterVisible = (activePageIdx != i && activePageIdx != i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private LauncherPage _activePage = null;
|
private LauncherPage _activePage = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,6 @@ namespace SourceGit.ViewModels
|
||||||
set => SetProperty(ref _data, value);
|
set => SetProperty(ref _data, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTabSplitterVisible
|
|
||||||
{
|
|
||||||
get => _isTabSplitterVisible;
|
|
||||||
set => SetProperty(ref _isTabSplitterVisible, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AvaloniaList<Notification> Notifications
|
public AvaloniaList<Notification> Notifications
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
|
@ -55,6 +49,5 @@ namespace SourceGit.ViewModels
|
||||||
|
|
||||||
private RepositoryNode _node = null;
|
private RepositoryNode _node = null;
|
||||||
private object _data = null;
|
private object _data = null;
|
||||||
private bool _isTabSplitterVisible = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,7 @@
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Menu (Windows/Linux) -->
|
<!-- Menu (Windows/Linux) -->
|
||||||
<Button Grid.Column="0" Classes="icon_button" VerticalAlignment="Bottom" IsVisible="{OnPlatform True, macOS=False}">
|
<Button Grid.Column="0" Classes="icon_button" VerticalAlignment="Bottom" Margin="6,0,2,3" IsVisible="{OnPlatform True, macOS=False}">
|
||||||
<Button.Margin>
|
|
||||||
<OnPlatform Default="4,0,2,3" macOS="4,0,6,3"/>
|
|
||||||
</Button.Margin>
|
|
||||||
|
|
||||||
<Button.Flyout>
|
<Button.Flyout>
|
||||||
<MenuFlyout Placement="BottomEdgeAlignedLeft" VerticalOffset="-8">
|
<MenuFlyout Placement="BottomEdgeAlignedLeft" VerticalOffset="-8">
|
||||||
<MenuItem Header="{DynamicResource Text.Preference}" Command="{x:Static s:App.OpenPreferenceCommand}" InputGesture="Ctrl+Shift+P">
|
<MenuItem Header="{DynamicResource Text.Preference}" Command="{x:Static s:App.OpenPreferenceCommand}" InputGesture="Ctrl+Shift+P">
|
||||||
|
|
|
@ -15,17 +15,20 @@
|
||||||
</RepeatButton>
|
</RepeatButton>
|
||||||
|
|
||||||
<ScrollViewer Grid.Column="1"
|
<ScrollViewer Grid.Column="1"
|
||||||
|
Margin="6,0"
|
||||||
x:Name="LauncherTabsScroller"
|
x:Name="LauncherTabsScroller"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
HorizontalScrollBarVisibility="Hidden"
|
HorizontalScrollBarVisibility="Hidden"
|
||||||
VerticalScrollBarVisibility="Disabled"
|
VerticalScrollBarVisibility="Disabled"
|
||||||
PointerWheelChanged="ScrollTabs"
|
PointerWheelChanged="ScrollTabs">
|
||||||
LayoutUpdated="OnTabsLayoutUpdated">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<ListBox Classes="launcher_page_tabbar"
|
<ListBox x:Name="LauncherTabsList"
|
||||||
|
Classes="launcher_page_tabbar"
|
||||||
ItemsSource="{Binding Pages}"
|
ItemsSource="{Binding Pages}"
|
||||||
SelectionMode="AlwaysSelected"
|
SelectionMode="AlwaysSelected"
|
||||||
SelectedItem="{Binding ActivePage, Mode=TwoWay}">
|
SelectedItem="{Binding ActivePage, Mode=TwoWay}"
|
||||||
|
SelectionChanged="OnTabsSelectionChanged"
|
||||||
|
LayoutUpdated="OnTabsLayoutUpdated">
|
||||||
<ListBox.ItemsPanel>
|
<ListBox.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<StackPanel Orientation="Horizontal" Height="30"/>
|
<StackPanel Orientation="Horizontal" Height="30"/>
|
||||||
|
@ -34,10 +37,8 @@
|
||||||
|
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate DataType="{x:Type vm:LauncherPage}">
|
<DataTemplate DataType="{x:Type vm:LauncherPage}">
|
||||||
<Border Classes="launcher_pagetab"
|
<Border Height="30"
|
||||||
Height="30"
|
Background="Transparent"
|
||||||
BorderThickness="1,1,1,0"
|
|
||||||
CornerRadius="6,6,0,0"
|
|
||||||
PointerPressed="OnPointerPressedTab"
|
PointerPressed="OnPointerPressedTab"
|
||||||
PointerMoved="OnPointerMovedOverTab"
|
PointerMoved="OnPointerMovedOverTab"
|
||||||
PointerReleased="OnPointerReleasedTab"
|
PointerReleased="OnPointerReleasedTab"
|
||||||
|
@ -86,11 +87,6 @@
|
||||||
</ToolTip.Tip>
|
</ToolTip.Tip>
|
||||||
<Path Width="8" Height="8" Data="{StaticResource Icons.Window.Close}"/>
|
<Path Width="8" Height="8" Data="{StaticResource Icons.Window.Close}"/>
|
||||||
</Button>
|
</Button>
|
||||||
<Rectangle Grid.Column="2"
|
|
||||||
Width=".5" Height="20"
|
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
|
||||||
Fill="{DynamicResource Brush.FG2}"
|
|
||||||
IsVisible="{Binding IsTabSplitterVisible}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
namespace SourceGit.Views
|
namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
|
@ -14,6 +15,98 @@ namespace SourceGit.Views
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
var activeStartX = selected.Bounds.X;
|
||||||
|
var activeEndX = activeStartX + selected.Bounds.Width;
|
||||||
|
if (activeStartX > endX + 6 || activeEndX < startX - 6)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var geo = new StreamGeometry();
|
||||||
|
var angle = Math.PI / 2;
|
||||||
|
var x = 0.0;
|
||||||
|
var y = height + 0.5;
|
||||||
|
using (var ctx = geo.Open())
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
y = 0;
|
||||||
|
ctx.LineTo(new Point(x, y));
|
||||||
|
x = drawRightX - 6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x = drawLeftX - 6;
|
||||||
|
ctx.BeginFigure(new Point(x, y), true);
|
||||||
|
x = drawLeftX;
|
||||||
|
y -= 6;
|
||||||
|
ctx.ArcTo(new Point(x, y), new Size(6.5, 6.5), angle, false, SweepDirection.CounterClockwise);
|
||||||
|
y = 6;
|
||||||
|
ctx.LineTo(new Point(x, y));
|
||||||
|
x += 6;
|
||||||
|
y = 0;
|
||||||
|
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);
|
||||||
|
y = height - 6;
|
||||||
|
ctx.LineTo(new Point(x, y));
|
||||||
|
x += 6;
|
||||||
|
y = height + 0.5;
|
||||||
|
ctx.ArcTo(new Point(x, y), new Size(6.5, 6.5), angle, false, SweepDirection.CounterClockwise);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x = LauncherTabsScroller.Bounds.Right;
|
||||||
|
ctx.LineTo(new Point(x, y));
|
||||||
|
y = height + 0.5;
|
||||||
|
ctx.LineTo(new Point(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fill = this.FindResource("Brush.ToolBar") as IBrush;
|
||||||
|
var stroke = new Pen(this.FindResource("Brush.Border0") as IBrush, 1);
|
||||||
|
context.DrawGeometry(fill, stroke, geo);
|
||||||
|
}
|
||||||
|
|
||||||
private void ScrollTabs(object sender, PointerWheelEventArgs e)
|
private void ScrollTabs(object sender, PointerWheelEventArgs e)
|
||||||
{
|
{
|
||||||
if (!e.KeyModifiers.HasFlag(KeyModifiers.Shift))
|
if (!e.KeyModifiers.HasFlag(KeyModifiers.Shift))
|
||||||
|
@ -52,6 +145,13 @@ namespace SourceGit.Views
|
||||||
LeftScrollIndicator.IsVisible = false;
|
LeftScrollIndicator.IsVisible = false;
|
||||||
RightScrollIndicator.IsVisible = false;
|
RightScrollIndicator.IsVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InvalidateVisual();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTabsSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
InvalidateVisual();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetupDragAndDrop(object sender, RoutedEventArgs e)
|
private void SetupDragAndDrop(object sender, RoutedEventArgs e)
|
||||||
|
|
Loading…
Reference in a new issue