Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for RTL Layout #331

Merged
merged 9 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions SukiUI.Demo/App.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
xmlns:common="clr-namespace:SukiUI.Demo.Common"
xmlns:suki="https://github.com/kikipoulet/SukiUI"
RequestedThemeVariant="Default">
Expand Down Expand Up @@ -42,6 +43,9 @@
</Application.Resources>
<Application.Styles>
<FluentTheme></FluentTheme>
<Style Selector="avaloniaEdit|TextEditor">
<Setter Property="FlowDirection" Value="LeftToRight"/>
</Style>
<StyleInclude Source="avares://SukiUI.Dock/Index.axaml" />
<StyleInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml" />
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml" />
Expand All @@ -50,6 +54,7 @@
<StyleInclude Source="avares://SukiUI.Demo/Styles/WrapPanelStyles.axaml" />
<StyleInclude Source="avares://SukiUI.Demo/Styles/TextStyles.axaml" />
<StyleInclude Source="avares://SukiUI.Demo/Styles/GlassCardStyles.axaml" />
<StyleInclude Source="avares://SukiUI.Demo/Styles/MaterialIconStyles.axaml" />
<avalonia:MaterialIconStyles />
</Application.Styles>

Expand Down
17 changes: 17 additions & 0 deletions SukiUI.Demo/Styles/MaterialIconStyles.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:icons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia">
<Design.PreviewWith>
<Border Padding="20">
<StackPanel FlowDirection="RightToLeft">
<icons:MaterialIcon Kind="Check" />
<icons:MaterialIcon Kind="Check" Classes="Flippable" />
</StackPanel>
</Border>
</Design.PreviewWith>

<Style Selector="icons|MaterialIcon:not(.Flippable)">
<Setter Property="FlowDirection" Value="LeftToRight"/>
</Style>

</Styles>
16 changes: 12 additions & 4 deletions SukiUI.Demo/SukiUIDemoView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<Grid ColumnDefinitions="*,20,*">

<suki:GlassCard CornerRadius="15" Padding="15">
<DockPanel FlowDirection="LeftToRight">
<DockPanel>
<Border Background="{DynamicResource SukiPrimaryColor}" Height="35" Padding="0" CornerRadius="100" Width="35">
<avalonia:MaterialIcon Height="20" Width="20" Kind="Wifi" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"></avalonia:MaterialIcon>
</Border>
Expand All @@ -70,7 +70,7 @@
</DockPanel>
</suki:GlassCard>
<suki:GlassCard Grid.Column="2" CornerRadius="15" Padding="15">
<DockPanel FlowDirection="LeftToRight">
<DockPanel>
<suki:GlassCard Height="35" Padding="0" CornerRadius="100" Width="35">
<avalonia:MaterialIcon Height="20" Width="20" HorizontalAlignment="Center" VerticalAlignment="Center" Kind="Bluetooth" Foreground="{DynamicResource SukiText}"></avalonia:MaterialIcon>
</suki:GlassCard>
Expand All @@ -82,7 +82,7 @@

<suki:GlassCard CornerRadius="15" Padding="15">

<DockPanel FlowDirection="LeftToRight">
<DockPanel>
<TextBlock Text="Volume" Margin="0,0,0,10" FontSize="15"
DockPanel.Dock="Top" FontWeight="DemiBold"
VerticalAlignment="Center" />
Expand All @@ -94,7 +94,7 @@
</suki:GlassCard>

<suki:GlassCard CornerRadius="15" Padding="15">
<DockPanel FlowDirection="LeftToRight">
<DockPanel>
<ToggleSwitch VerticalAlignment="Center" DockPanel.Dock="Right" IsChecked="True" />
<TextBlock DockPanel.Dock="Bottom" Text="Disable notifications." Margin="0,8,0,0"
Foreground="{DynamicResource SukiLowText}"
Expand Down Expand Up @@ -138,6 +138,14 @@
Kind="{Binding TitleBarVisible, Converter={x:Static converters:BoolToIconConverters.Visibility}}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Command="{Binding ToggleRightToLeftCommand}"
Header="Right To Left"
ToolTip.Tip="Toggles the right to left.">
<MenuItem.Icon>
<avalonia:MaterialIcon
Kind="SwapHorizontal" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Fullscreen"
PointerPressed="MakeFullScreenPressed"
ToolTip.Tip="Makes the app fullscreen." />
Expand Down
5 changes: 4 additions & 1 deletion SukiUI.Demo/SukiUIDemoViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ private void ToggleTitleBar()
.WithContent($"Window title bar has been {(TitleBarVisible ? "shown" : "hidden")}.")
.Queue();
}


[RelayCommand]
private void ToggleRightToLeft() => _theme.IsRightToLeft = !_theme.IsRightToLeft;

[RelayCommand]
private static void OpenUrl(string url) => UrlUtilities.OpenUrl(url);

Expand Down
1 change: 1 addition & 0 deletions SukiUI/Controls/CodeView.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public CodeView()
new PathIcon()
{
Data = Icons.ChevronRight,
Classes = { "Flippable" },
Foreground =
new Avalonia.Media.SolidColorBrush(
(Avalonia.Media.Color)Application.Current.FindRequiredResource("SukiText")),
Expand Down
2 changes: 1 addition & 1 deletion SukiUI/Controls/Stepper.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ private void AddStep(object step, int index, Grid grid, int stepCount)
};

var icon = new PathIcon
{ Height = 10, Width = 10, Data = Icons.ChevronRight, Margin = new Thickness(0, 0, 20, 0) };
{ Height = 10, Width = 10, Data = Icons.ChevronRight, Margin = new Thickness(0, 0, 20, 0), Classes = { "Flippable" } };
if (index == stepCount - 1)
{
icon.IsVisible = false;
Expand Down
1 change: 1 addition & 0 deletions SukiUI/Controls/SukiSideMenu.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
IsVisible="{TemplateBinding SidebarToggleEnabled}">
<Panel>
<PathIcon Name="PART_ExpandIcon"
Classes="Flippable"
Width="12"
Height="12"
Data="{x:Static content:Icons.ChevronLeft}"
Expand Down
3 changes: 2 additions & 1 deletion SukiUI/Controls/SukiStackPage.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ private void AddChevron()
Width = 15,
Margin = new Thickness(15, -3, 15, 0),
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
Classes = { "Flippable" }
};
_disposables.Push(pathIcon.Bind(ForegroundProperty, ColorResource));
_stackHeaders!.Children.Add(pathIcon);
Expand Down
32 changes: 16 additions & 16 deletions SukiUI/Controls/SukiWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<Setter Property="ExtendClientAreaTitleBarHeightHint" Value="-1" />
<Setter Property="ExtendClientAreaToDecorationsHint" Value="True" />
<Setter Property="TextElement.Foreground" Value="{DynamicResource SukiText}" />
<Setter Property="FlowDirection" Value="{DynamicResource FlowDirectionPrimary}" />
<Setter Property="Template">
<ControlTemplate>
<Border Margin="{TemplateBinding Margin}"
Expand Down Expand Up @@ -62,7 +63,6 @@
<DockPanel Margin="12,9" LastChildFill="True">
<StackPanel VerticalAlignment="Center"
DockPanel.Dock="Right"
FlowDirection="RightToLeft"
Orientation="Horizontal"
Spacing="7">
<StackPanel.Styles>
Expand All @@ -71,14 +71,13 @@
<Setter Property="Width" Value="10" />
</Style>
</StackPanel.Styles>
<Button Name="PART_CloseButton" Classes="Basic Rounded WindowControlsButton Close">
<PathIcon Data="{x:Static icons:Icons.WindowClose}" />
</Button>
<Button Name="PART_MaximizeButton"
Classes="Basic Rounded WindowControlsButton"
IsVisible="{TemplateBinding CanMaximize}">
<PathIcon x:Name="MaximizeIcon" Data="{x:Static icons:Icons.WindowMaximize}" />
</Button>
<ItemsControl ItemsSource="{TemplateBinding RightWindowTitleBarControls}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel FlowDirection="{DynamicResource FlowDirectionOpposite}" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Button Name="PART_MinimizeButton"
VerticalContentAlignment="Bottom"
Classes="Basic Rounded WindowControlsButton"
Expand All @@ -87,13 +86,14 @@
VerticalAlignment="Bottom"
Data="{x:Static icons:Icons.WindowMinimize}" />
</Button>
<ItemsControl ItemsSource="{TemplateBinding RightWindowTitleBarControls}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel FlowDirection="RightToLeft" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Button Name="PART_MaximizeButton"
Classes="Basic Rounded WindowControlsButton"
IsVisible="{TemplateBinding CanMaximize}">
<PathIcon x:Name="MaximizeIcon" Data="{x:Static icons:Icons.WindowMaximize}" />
</Button>
<Button Name="PART_CloseButton" Classes="Basic Rounded WindowControlsButton Close">
<PathIcon Data="{x:Static icons:Icons.WindowClose}" />
</Button>
</StackPanel>
<StackPanel VerticalAlignment="Center"
IsHitTestVisible="False"
Expand Down
9 changes: 6 additions & 3 deletions SukiUI/Controls/SukiWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,15 @@ private void EnableWindowsSnapLayout(Button maximize)
var point = new PixelPoint(
(short)(ToInt32(lParam) & 0xffff),
(short)(ToInt32(lParam) >> 16));
var buttonLeftTop = maximize.PointToScreen(new Point(0, 0));
var desiredSize = maximize.DesiredSize;
var buttonLeftTop = maximize.PointToScreen(FlowDirection == FlowDirection.LeftToRight
? new Point(desiredSize.Width, 0)
: new Point(0, 0));
var x = (buttonLeftTop.X - point.X) / RenderScaling;
var y = (point.Y - buttonLeftTop.Y) / RenderScaling;
if (new Rect(0, 0,
maximize.DesiredSize.Width,
maximize.DesiredSize.Height)
desiredSize.Width,
desiredSize.Height)
.Contains(new Point(x, y)))
{
setter?.SetValue(maximize, true);
Expand Down
4 changes: 2 additions & 2 deletions SukiUI/Theme/Calendar/CalendarItem.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@
<Button Name="PART_PreviousButton"
Grid.Column="2"
Classes="NavButton Basic">
<PathIcon Classes="Primary" Data="{x:Static content:Icons.ChevronLeft}" />
<PathIcon Classes="Primary Flippable" Data="{x:Static content:Icons.ChevronLeft}" />
</Button>

<Button Name="PART_NextButton"
Grid.Column="3"
Classes="NavButton Basic">
<PathIcon Classes="Primary" Data="{x:Static content:Icons.ChevronRight}" />
<PathIcon Classes="Primary Flippable" Data="{x:Static content:Icons.ChevronRight}" />
</Button>
</Grid>

Expand Down
1 change: 1 addition & 0 deletions SukiUI/Theme/CheckBoxStyles.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
Margin="1,1,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FlowDirection="LeftToRight"
Data="M 1145.607177734375,430 C1145.607177734375,430 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1138,434.5538330078125 1138,434.5538330078125 1138,434.5538330078125 1141.482177734375,438 1141.482177734375,438 1141.482177734375,438 1141.96875,437.9375 1141.96875,437.9375 1141.96875,437.9375 1147,431.34619140625 1147,431.34619140625 1147,431.34619140625 1145.607177734375,430 1145.607177734375,430 z"
Fill="White"
Stretch="Uniform" >
Expand Down
2 changes: 2 additions & 0 deletions SukiUI/Theme/Index.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
<StyleInclude Source="avares://SukiUI/Theme/ScrollViewerStyles.axaml" />
<StyleInclude Source="avares://SukiUI/Theme/ButtonLoadingStyles.axaml" />

<StyleInclude Source="avares://SukiUI/Theme/PathIconStyles.axaml" />

<Styles.Resources>
<ResourceDictionary>

Expand Down
31 changes: 31 additions & 0 deletions SukiUI/Theme/Index.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public partial class SukiTheme : Styles
AvaloniaProperty.Register<SukiTheme, SukiColor>(nameof(Color), defaultBindingMode: BindingMode.OneTime,
defaultValue: SukiColor.Blue);

public static readonly StyledProperty<bool> IsRightToLeftProperty =
AvaloniaProperty.Register<SukiTheme, bool>(nameof(IsRightToLeft), defaultBindingMode: BindingMode.OneTime,
defaultValue: false);

/// <summary>
/// Used to assign the ColorTheme at launch,
/// </summary>
Expand All @@ -32,6 +36,12 @@ public SukiColor ThemeColor
}
}

public bool IsRightToLeft
{
get => GetValue(IsRightToLeftProperty);
set => SetValue(IsRightToLeftProperty, value);
}

/// <summary>
/// Called whenever the application's <see cref="SukiColorTheme"/> is changed.
/// Useful where controls cannot use "DynamicResource"
Expand Down Expand Up @@ -74,6 +84,18 @@ public SukiTheme()
_app.ActualThemeVariantChanged += (_, e) => OnBaseThemeChanged?.Invoke(_app.ActualThemeVariant);
foreach (var theme in DefaultColorThemes)
AddColorTheme(theme.Value);

UpdateFlowDirectionResources(IsRightToLeft);
}

protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
if (change.Property == IsRightToLeftProperty)
{
UpdateFlowDirectionResources(change.GetNewValue<bool>());
}

base.OnPropertyChanged(change);
}

/// <summary>
Expand Down Expand Up @@ -152,6 +174,15 @@ public void SwitchBaseTheme()
Application.Current.RequestedThemeVariant = newBase;
}

private void UpdateFlowDirectionResources(bool rightToLeft)
{
var primary = rightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
var opposite = rightToLeft ? FlowDirection.LeftToRight : FlowDirection.RightToLeft;

Resources["FlowDirectionPrimary"] = primary;
Resources["FlowDirectionOpposite"] = opposite;
}

/// <summary>
/// Initializes the color theme resources whenever the property is changed.
/// In an ideal world people wouldn't use the property
Expand Down
3 changes: 2 additions & 1 deletion SukiUI/Theme/ManagedFileChooserConverters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public class TextToPathConverter : IValueConverter
var p = new PathIcon
{
Height = 6, Margin = new Thickness(12, 4, 12, 2), Width = 5, Data = Icons.ChevronRight,
IsVisible = i != pathes.Length - 1, VerticalAlignment = VerticalAlignment.Center
IsVisible = i != pathes.Length - 1, VerticalAlignment = VerticalAlignment.Center,
Classes = { "Flippable" }
};

p[!TemplatedControl.ForegroundProperty] = new DynamicResourceExtension("SukiLowText");
Expand Down
5 changes: 5 additions & 0 deletions SukiUI/Theme/MenuItem.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
DockPanel.Dock="Left"
Fill="{DynamicResource SukiLightBorderBrush}" />
<PathIcon Name="PART_RightArrow"
Classes="Flippable"
Width="8"
Height="8"
Margin="5,0,10,0"
Expand Down Expand Up @@ -149,6 +150,10 @@
</ControlTemplate>
</Setter>
</Style>

<Style Selector="^[FlowDirection=RightToLeft] /template/ Popup#PART_Popup">
<Setter Property="HorizontalOffset" Value="25"/>
</Style>
</ControlTheme>
<ControlTheme x:Key="{x:Type MenuItem}"
BasedOn="{StaticResource SukiMenuItemStyle}"
Expand Down
Loading