refactor: custom renderer for launcher tab bar

This commit is contained in:
leo 2024-06-29 17:04:39 +08:00
parent bfea573d4b
commit 1241539260
No known key found for this signature in database
6 changed files with 112 additions and 47 deletions

View file

@ -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"/>

View file

@ -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,11 +66,9 @@ 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];
} }
} }
}
public void Quit() public void Quit()
{ {
@ -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;
} }
} }

View file

@ -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;
} }
} }

View file

@ -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">

View file

@ -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>

View file

@ -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)