diff --git a/UniSky/App.xaml b/UniSky/App.xaml
index 2b36f20..d23ab64 100644
--- a/UniSky/App.xaml
+++ b/UniSky/App.xaml
@@ -22,15 +22,18 @@
-
+
+
+ #10FFFFFF
+ #10000000
@@ -48,6 +51,9 @@
14
14
+
+
diff --git a/UniSky/Controls/Compose/ComposeSheet.xaml b/UniSky/Controls/Compose/ComposeSheet.xaml
index 2398315..e679f3e 100644
--- a/UniSky/Controls/Compose/ComposeSheet.xaml
+++ b/UniSky/Controls/Compose/ComposeSheet.xaml
@@ -5,7 +5,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UniSky.Controls.Compose"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:converters="using:UniSky.Converters"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:converters="using:UniSky.Converters"
+ xmlns:extensions="using:UniSky.Extensions" xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="320"
Style="{StaticResource DefaultSheetControlStyle}"
DataContext="{x:Bind ViewModel, Mode=OneWay}"
@@ -26,6 +28,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UniSky/Controls/Compose/ComposeSheet.xaml.cs b/UniSky/Controls/Compose/ComposeSheet.xaml.cs
index 38be769..d5bdbd8 100644
--- a/UniSky/Controls/Compose/ComposeSheet.xaml.cs
+++ b/UniSky/Controls/Compose/ComposeSheet.xaml.cs
@@ -79,13 +79,17 @@ private async void OnHiding(SheetControl sender, SheetHidingEventArgs e)
private void OnInputPaneShowing(InputPane sender, InputPaneVisibilityEventArgs args)
{
- ContentGrid.Padding = new Thickness(0, 0, 0, args.OccludedRect.Top - args.OccludedRect.Bottom);
+ if (ActualWidth > 620) return;
+
+ ContentGrid.Padding = new Thickness(0, 0, 0, args.OccludedRect.Height);
args.EnsuredFocusedElementInView = true;
}
private void OnInputPaneHiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
- ContentGrid.Padding = new Thickness(0, 0, 0, args.OccludedRect.Top - args.OccludedRect.Bottom);
+ if (ActualWidth > 620) return;
+
+ ContentGrid.Padding = new Thickness(0, 0, 0, args.OccludedRect.Height);
args.EnsuredFocusedElementInView = true;
}
diff --git a/UniSky/Controls/RadialProgressBar/RadialProgressBar.cs b/UniSky/Controls/RadialProgressBar/RadialProgressBar.cs
new file mode 100644
index 0000000..d163e25
--- /dev/null
+++ b/UniSky/Controls/RadialProgressBar/RadialProgressBar.cs
@@ -0,0 +1,202 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+
+namespace Microsoft.Toolkit.Uwp.UI.Controls
+{
+ ///
+ /// An alternative implementation of a progress bar.
+ /// Progression is represented by a loop filling up in a clockwise fashion.
+ /// Like the traditional progress bar, it inherits from RangeBase, so Minimum, Maximum and Value properties work the same way.
+ ///
+ [TemplatePart(Name = OutlineFigurePartName, Type = typeof(PathFigure))]
+ [TemplatePart(Name = OutlineArcPartName, Type = typeof(ArcSegment))]
+ [TemplatePart(Name = BarFigurePartName, Type = typeof(PathFigure))]
+ [TemplatePart(Name = BarArcPartName, Type = typeof(ArcSegment))]
+ [TemplatePart(Name = TextPartName, Type = typeof(TextBlock))]
+ public partial class RadialProgressBar : ProgressBar
+ {
+ private const string OutlineFigurePartName = "OutlineFigurePart";
+ private const string OutlineArcPartName = "OutlineArcPart";
+ private const string BarFigurePartName = "BarFigurePart";
+ private const string BarArcPartName = "BarArcPart";
+ private const string TextPartName = "ProgressTextPart";
+
+ private PathFigure outlineFigure;
+ private PathFigure barFigure;
+ private ArcSegment outlineArc;
+ private ArcSegment barArc;
+ private TextBlock textBlock;
+
+ private bool allTemplatePartsDefined = false;
+
+ ///
+ /// Called when the Minimum property changes.
+ ///
+ /// Old value of the Minimum property.
+ /// New value of the Minimum property.
+ protected override void OnMinimumChanged(double oldMinimum, double newMinimum)
+ {
+ base.OnMinimumChanged(oldMinimum, newMinimum);
+ RenderSegment();
+ }
+
+ ///
+ /// Called when the Maximum property changes.
+ ///
+ /// Old value of the Maximum property.
+ /// New value of the Maximum property.
+ protected override void OnMaximumChanged(double oldMaximum, double newMaximum)
+ {
+ base.OnMaximumChanged(oldMaximum, newMaximum);
+ RenderSegment();
+ }
+
+ ///
+ /// Called when the Value property changes.
+ ///
+ /// Old value of the Value property.
+ /// New value of the Value property.
+ protected override void OnValueChanged(double oldValue, double newValue)
+ {
+ base.OnValueChanged(oldValue, newValue);
+ RenderSegment();
+ }
+
+ ///
+ /// Update the visual state of the control when its template is changed.
+ ///
+ protected override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ outlineFigure = GetTemplateChild(OutlineFigurePartName) as PathFigure;
+ outlineArc = GetTemplateChild(OutlineArcPartName) as ArcSegment;
+ barFigure = GetTemplateChild(BarFigurePartName) as PathFigure;
+ barArc = GetTemplateChild(BarArcPartName) as ArcSegment;
+ textBlock = GetTemplateChild(TextPartName) as TextBlock;
+
+ allTemplatePartsDefined = outlineFigure != null && outlineArc != null && barFigure != null && barArc != null && textBlock != null;
+
+ RenderAll();
+ }
+
+ ///
+ /// Gets or sets the thickness of the circular outline and segment
+ ///
+ public double Thickness
+ {
+ get { return (double)GetValue(ThicknessProperty); }
+ set { SetValue(ThicknessProperty, value); }
+ }
+
+ ///
+ /// Identifies the Thickness dependency property
+ ///
+ public static readonly DependencyProperty ThicknessProperty = DependencyProperty.Register(nameof(Thickness), typeof(double), typeof(RadialProgressBar), new PropertyMetadata(0.0, ThicknessChangedHandler));
+
+ ///
+ /// Gets or sets the color of the circular outline on which the segment is drawn
+ ///
+ public Brush Outline
+ {
+ get { return (Brush)GetValue(OutlineProperty); }
+ set { SetValue(OutlineProperty, value); }
+ }
+
+ ///
+ /// Identifies the Outline dependency property
+ ///
+ public static readonly DependencyProperty OutlineProperty = DependencyProperty.Register(nameof(Outline), typeof(Brush), typeof(RadialProgressBar), new PropertyMetadata(new SolidColorBrush(Windows.UI.Colors.Transparent)));
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Create a default circular progress bar
+ ///
+ public RadialProgressBar()
+ {
+ DefaultStyleKey = typeof(RadialProgressBar);
+ SizeChanged += SizeChangedHandler;
+ }
+
+ // Render outline and progress segment when thickness is changed
+ private static void ThicknessChangedHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var sender = d as RadialProgressBar;
+ sender.RenderAll();
+ }
+
+ // Render outline and progress segment when control is resized.
+ private void SizeChangedHandler(object sender, SizeChangedEventArgs e)
+ {
+ var self = sender as RadialProgressBar;
+ self.RenderAll();
+ }
+
+ private double ComputeNormalizedRange()
+ {
+ var range = Maximum - Minimum;
+ var delta = Value - Minimum;
+ var output = range == 0.0 ? 0.0 : delta / range;
+ output = Math.Min(Math.Max(0.0, output), 0.9999);
+ return output;
+ }
+
+ // Compute size of ellipse so that the outer edge touches the bounding rectangle
+ private Size ComputeEllipseSize()
+ {
+ var safeThickness = Math.Max(Thickness, 0.0);
+ var width = Math.Max((ActualWidth - safeThickness) / 2.0, 0.0);
+ var height = Math.Max((ActualHeight - safeThickness) / 2.0, 0.0);
+ return new Size(width, height);
+ }
+
+ // Render the segment representing progress ratio.
+ private void RenderSegment()
+ {
+ if (!allTemplatePartsDefined)
+ {
+ return;
+ }
+
+ textBlock.Text = ((int)(Maximum - Value)).ToString();
+
+ var normalizedRange = ComputeNormalizedRange();
+
+ var angle = 2 * Math.PI * normalizedRange;
+ var size = ComputeEllipseSize();
+ var translationFactor = Math.Max(Thickness / 2.0, 0.0);
+
+ double x = (Math.Sin(angle) * size.Width) + size.Width + translationFactor;
+ double y = (((Math.Cos(angle) * size.Height) - size.Height) * -1) + translationFactor;
+
+ barArc.IsLargeArc = angle >= Math.PI;
+ barArc.Point = new Point(x, y);
+ }
+
+ // Render the progress segment and the loop outline. Needs to run when control is resized or retemplated
+ private void RenderAll()
+ {
+ if (!allTemplatePartsDefined)
+ {
+ return;
+ }
+
+ var size = ComputeEllipseSize();
+ var segmentWidth = size.Width;
+ var translationFactor = Math.Max(Thickness / 2.0, 0.0);
+
+ outlineFigure.StartPoint = barFigure.StartPoint = new Point(segmentWidth + translationFactor, translationFactor);
+ outlineArc.Size = barArc.Size = new Size(segmentWidth, size.Height);
+ outlineArc.Point = new Point(segmentWidth + translationFactor - 0.05, translationFactor);
+
+ RenderSegment();
+ }
+ }
+}
\ No newline at end of file
diff --git a/UniSky/Controls/RadialProgressBar/RadialProgressBar.xaml b/UniSky/Controls/RadialProgressBar/RadialProgressBar.xaml
new file mode 100644
index 0000000..8b2f8c5
--- /dev/null
+++ b/UniSky/Controls/RadialProgressBar/RadialProgressBar.xaml
@@ -0,0 +1,63 @@
+
+
+
+
\ No newline at end of file
diff --git a/UniSky/Templates/SheetControlStyles.xaml b/UniSky/Controls/Sheet/SheetControl.xaml
similarity index 100%
rename from UniSky/Templates/SheetControlStyles.xaml
rename to UniSky/Controls/Sheet/SheetControl.xaml
diff --git a/UniSky/Controls/Sheet/SheetRootControl.xaml.cs b/UniSky/Controls/Sheet/SheetRootControl.xaml.cs
index 08741ce..098fd5c 100644
--- a/UniSky/Controls/Sheet/SheetRootControl.xaml.cs
+++ b/UniSky/Controls/Sheet/SheetRootControl.xaml.cs
@@ -13,6 +13,8 @@
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Composition;
+using Windows.UI.Core;
+using Windows.UI.Core.Preview;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
@@ -58,12 +60,16 @@ public SheetRootControl()
protected override Size ArrangeOverride(Size finalSize)
{
- if (finalSize.Width > 620)
+ if (!double.IsInfinity(HostControl.MaxHeight))
+ {
TotalHeight = 64;
+ SheetRoot.Height = Math.Max(0, HostControl.MaxHeight - (SheetBorder.Margin.Top + SheetBorder.Margin.Bottom) - (HostControl.Margin.Top + HostControl.Margin.Bottom));
+ }
else
+ {
TotalHeight = finalSize.Height;
-
- SheetRoot.Height = Math.Max(0, finalSize.Height - (SheetBorder.Margin.Top + SheetBorder.Margin.Bottom));
+ SheetRoot.Height = Math.Max(0, finalSize.Height - (SheetBorder.Margin.Top + SheetBorder.Margin.Bottom) - (HostControl.Margin.Top + HostControl.Margin.Bottom));
+ }
return base.ArrangeOverride(finalSize);
}
@@ -76,8 +82,18 @@ internal void ShowSheet(SheetControl control)
VisualStateManager.GoToState(this, "Open", true);
Window.Current.SetTitleBar(TitleBar);
+
var safeAreaService = Ioc.Default.GetRequiredService();
safeAreaService.SafeAreaUpdated += OnSafeAreaUpdated;
+
+ var systemNavigationManager = SystemNavigationManager.GetForCurrentView();
+ systemNavigationManager.BackRequested += OnBackRequested;
+ }
+
+ private async void OnBackRequested(object sender, BackRequestedEventArgs e)
+ {
+ e.Handled = true;
+ await HideSheetAsync();
}
internal async Task HideSheetAsync()
@@ -94,6 +110,9 @@ internal async Task HideSheetAsync()
var safeAreaService = Ioc.Default.GetRequiredService();
safeAreaService.SafeAreaUpdated -= OnSafeAreaUpdated;
+ var systemNavigationManager = SystemNavigationManager.GetForCurrentView();
+ systemNavigationManager.BackRequested -= OnBackRequested;
+
return true;
}
@@ -101,8 +120,16 @@ private void OnSafeAreaUpdated(object sender, SafeAreaUpdatedEventArgs e)
{
TitleBar.Height = e.SafeArea.Bounds.Top;
SheetBorder.Margin = new Thickness(0, 16 + e.SafeArea.Bounds.Top, 0, 0);
- SheetRoot.Height = Math.Max(0, ActualHeight - (SheetBorder.Margin.Top + SheetBorder.Margin.Bottom));
HostControl.Margin = new Thickness(e.SafeArea.Bounds.Left, 0, e.SafeArea.Bounds.Right, e.SafeArea.Bounds.Bottom);
+
+ if (!double.IsInfinity(HostControl.MaxHeight))
+ {
+ SheetRoot.Height = Math.Max(0, HostControl.MaxHeight - (SheetBorder.Margin.Top + SheetBorder.Margin.Bottom) - (HostControl.Margin.Top + HostControl.Margin.Bottom));
+ }
+ else
+ {
+ SheetRoot.Height = Math.Max(0, ActualHeight - (SheetBorder.Margin.Top + SheetBorder.Margin.Bottom) - (HostControl.Margin.Top + HostControl.Margin.Bottom));
+ }
}
private async void RefreshContainer_RefreshRequested(MUXC.RefreshContainer sender, MUXC.RefreshRequestedEventArgs args)
diff --git a/UniSky/Converters/Static.cs b/UniSky/Converters/Static.cs
index c06d66a..460ae07 100644
--- a/UniSky/Converters/Static.cs
+++ b/UniSky/Converters/Static.cs
@@ -16,7 +16,6 @@ public static bool Not(bool x)
=> !x;
public static bool NotNull(object x)
=> x is not null;
-
public static bool NotNullOrWhiteSpace(string s)
=> !string.IsNullOrWhiteSpace(s);
}
diff --git a/UniSky/Package.appxmanifest b/UniSky/Package.appxmanifest
index 2a4fe68..3a68deb 100644
--- a/UniSky/Package.appxmanifest
+++ b/UniSky/Package.appxmanifest
@@ -10,7 +10,7 @@
+ Version="1.0.123.0" />
diff --git a/UniSky/UniSky.csproj b/UniSky/UniSky.csproj
index a741b19..a06a24a 100644
--- a/UniSky/UniSky.csproj
+++ b/UniSky/UniSky.csproj
@@ -149,6 +149,7 @@
+
SheetRootControl.xaml
@@ -373,6 +374,10 @@
Designer
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
Designer
MSBuild:Compile
@@ -417,7 +422,7 @@
Designer
MSBuild:Compile
-
+
MSBuild:Compile
Designer
diff --git a/UniSky/ViewModels/Compose/ComposeViewModel.cs b/UniSky/ViewModels/Compose/ComposeViewModel.cs
index 2d31689..5c127e5 100644
--- a/UniSky/ViewModels/Compose/ComposeViewModel.cs
+++ b/UniSky/ViewModels/Compose/ComposeViewModel.cs
@@ -20,9 +20,12 @@ public partial class ComposeViewModel : ViewModelBase
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanPost))]
+ [NotifyPropertyChangedFor(nameof(Characters))]
private string _text;
[ObservableProperty]
private string _avatarUrl;
+ [ObservableProperty]
+ private int maxCharacters;
private readonly IProtocolService protocolService;
private readonly ILogger logger;
@@ -30,10 +33,11 @@ public partial class ComposeViewModel : ViewModelBase
// TODO: this but better
public bool IsDirty
=> !string.IsNullOrEmpty(Text);
-
// TODO: ditto
public bool CanPost
- => !string.IsNullOrEmpty(Text);
+ => !string.IsNullOrEmpty(Text) && Text.Length <= 300;
+ public int Characters
+ => Text?.Length ?? 0;
public ComposeViewModel(
IProtocolService protocolService,
@@ -42,6 +46,8 @@ public ComposeViewModel(
this.protocolService = protocolService;
this.logger = logger;
+ this.MaxCharacters = 300;
+
Task.Run(LoadAsync);
}