diff --git a/build/SourceLink.props b/build/SourceLink.props index 1e007e01eb..9f05848881 100644 --- a/build/SourceLink.props +++ b/build/SourceLink.props @@ -3,7 +3,6 @@ true false true - embedded $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb @@ -15,6 +14,10 @@ true + + embedded + + diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 505f486a6d..f6d382f99d 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -5,6 +5,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using Avalonia.Styling; +using Avalonia.Themes.Default; using Avalonia.Themes.Fluent; using ControlCatalog.ViewModels; @@ -29,6 +30,8 @@ namespace ControlCatalog public static FluentTheme Fluent = new FluentTheme(new Uri("avares://ControlCatalog/Styles")); + public static SimpleTheme Default = new SimpleTheme(new Uri("avares://ControlCatalog/Styles")); + public static Styles DefaultLight = new Styles { new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) @@ -43,14 +46,7 @@ namespace ControlCatalog { Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml") }, - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml") - } + Default }; public static Styles DefaultDark = new Styles @@ -67,14 +63,7 @@ namespace ControlCatalog { Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml") }, - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseDark.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml") - } + Default }; public override void Initialize() diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index 0579355831..79cf07c8d9 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -63,11 +63,13 @@ namespace ControlCatalog } else if (theme == CatalogTheme.DefaultLight) { + App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Light; Application.Current.Styles[0] = App.DefaultLight; Application.Current.Styles[1] = App.DataGridDefault; } else if (theme == CatalogTheme.DefaultDark) { + App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Dark; Application.Current.Styles[0] = App.DefaultDark; Application.Current.Styles[1] = App.DataGridDefault; } diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 9b7d37e108..a7560c37f2 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -36,6 +36,7 @@ CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.WindowBase' does not i InterfacesShouldHaveSameMembers : Interface member 'public System.EventHandler Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.ShutdownRequested' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.add_ShutdownRequested(System.EventHandler)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.remove_ShutdownRequested(System.EventHandler)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.TryShutdown(System.Int32)' is present in the implementation but not in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Embedding.EmbeddableControlRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber' in the implementation but it does in the contract. MembersMustExist : Member 'public System.Action Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.Resized.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.Resized.set(System.Action)' does not exist in the implementation but it does exist in the contract. @@ -62,4 +63,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract. -Total Issues: 63 +Total Issues: 64 diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 3a2fd68af5..edddf31d45 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -76,36 +76,21 @@ namespace Avalonia.Controls.ApplicationLifetimes return; if (ShutdownMode == ShutdownMode.OnLastWindowClose && _windows.Count == 0) - Shutdown(); - else if (ShutdownMode == ShutdownMode.OnMainWindowClose && window == MainWindow) - Shutdown(); + TryShutdown(); + else if (ShutdownMode == ShutdownMode.OnMainWindowClose && ReferenceEquals(window, MainWindow)) + TryShutdown(); } public void Shutdown(int exitCode = 0) { - if (_isShuttingDown) - throw new InvalidOperationException("Application is already shutting down."); - - _exitCode = exitCode; - _isShuttingDown = true; + DoShutdown(new ShutdownRequestedEventArgs(), true, exitCode); + } - try - { - foreach (var w in Windows) - w.Close(); - var e = new ControlledApplicationLifetimeExitEventArgs(exitCode); - Exit?.Invoke(this, e); - _exitCode = e.ApplicationExitCode; - } - finally - { - _cts?.Cancel(); - _cts = null; - _isShuttingDown = false; - } + public bool TryShutdown(int exitCode = 0) + { + return DoShutdown(new ShutdownRequestedEventArgs(), false, exitCode); } - public int Start(string[] args) { Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args)); @@ -114,7 +99,10 @@ namespace Avalonia.Controls.ApplicationLifetimes if(options != null && options.ProcessUrlActivationCommandLine && args.Length > 0) { - ((IApplicationPlatformEvents)Application.Current).RaiseUrlsOpened(args); + if (Application.Current is IApplicationPlatformEvents events) + { + events.RaiseUrlsOpened(args); + } } var lifetimeEvents = AvaloniaLocator.Current.GetService(); @@ -145,23 +133,57 @@ namespace Avalonia.Controls.ApplicationLifetimes if (_activeLifetime == this) _activeLifetime = null; } - - private void OnShutdownRequested(object sender, ShutdownRequestedEventArgs e) + + private bool DoShutdown(ShutdownRequestedEventArgs e, bool force = false, int exitCode = 0) { - ShutdownRequested?.Invoke(this, e); + if (!force) + { + ShutdownRequested?.Invoke(this, e); - if (e.Cancel) - return; + if (e.Cancel) + return false; + + if (_isShuttingDown) + throw new InvalidOperationException("Application is already shutting down."); + } + + _exitCode = exitCode; + _isShuttingDown = true; - // When an OS shutdown request is received, try to close all non-owned windows. Windows can cancel - // shutdown by setting e.Cancel = true in the Closing event. Owned windows will be shutdown by their - // owners. - foreach (var w in Windows) - if (w.Owner is null) - w.Close(); - if (Windows.Count > 0) - e.Cancel = true; + try + { + // When an OS shutdown request is received, try to close all non-owned windows. Windows can cancel + // shutdown by setting e.Cancel = true in the Closing event. Owned windows will be shutdown by their + // owners. + foreach (var w in Windows) + { + if (w.Owner is null) + { + w.Close(); + } + } + + if (!force && Windows.Count > 0) + { + e.Cancel = true; + return false; + } + + var args = new ControlledApplicationLifetimeExitEventArgs(exitCode); + Exit?.Invoke(this, args); + _exitCode = args.ApplicationExitCode; + } + finally + { + _cts?.Cancel(); + _cts = null; + _isShuttingDown = false; + } + + return true; } + + private void OnShutdownRequested(object sender, ShutdownRequestedEventArgs e) => DoShutdown(e); } public class ClassicDesktopStyleApplicationLifetimeOptions diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs index a70d5dd2f1..a83229b732 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs @@ -9,6 +9,12 @@ namespace Avalonia.Controls.ApplicationLifetimes /// public interface IClassicDesktopStyleApplicationLifetime : IControlledApplicationLifetime { + /// + /// Tries to Shutdown the application. event can be used to cancel the shutdown. + /// + /// An integer exit code for an application. The default exit code is 0. + bool TryShutdown(int exitCode = 0); + /// /// Gets the arguments passed to the /// diff --git a/src/Avalonia.Controls/LayoutTransformControl.cs b/src/Avalonia.Controls/LayoutTransformControl.cs index 83ad2b3638..a8e15ee463 100644 --- a/src/Avalonia.Controls/LayoutTransformControl.cs +++ b/src/Avalonia.Controls/LayoutTransformControl.cs @@ -18,7 +18,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(LayoutTransform)); public static readonly StyledProperty UseRenderTransformProperty = - AvaloniaProperty.Register(nameof(LayoutTransform)); + AvaloniaProperty.Register(nameof(UseRenderTransform)); static LayoutTransformControl() { diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index a2bdcd1ea8..18348571bf 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -101,7 +101,7 @@ namespace Avalonia.Controls.Platform root.Deactivated -= WindowDeactivated; } - if (_root is TopLevel tl) + if (_root is TopLevel tl && tl.PlatformImpl != null) tl.PlatformImpl.LostFocus -= TopLevelLostPlatformFocus; _inputManagerSubscription?.Dispose(); diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 1a11778db2..c59c7c11d9 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -44,7 +44,7 @@ namespace Avalonia.Controls.Primitives /// The dependency resolver to use. If null the default dependency resolver will be used. /// public PopupRoot(TopLevel parent, IPopupImpl impl, IAvaloniaDependencyResolver dependencyResolver) - : base(impl, dependencyResolver) + : base(ValidatingPopupImpl.Wrap(impl), dependencyResolver) { _parent = parent; } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index eaee5bdb50..9eb35e0548 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -134,6 +134,8 @@ namespace Avalonia.Controls "Could not create window implementation: maybe no windowing subsystem was initialized?"); } + impl = ValidatingToplevelImpl.Wrap(impl); + PlatformImpl = impl; _actualTransparencyLevel = PlatformImpl.TransparencyLevel; @@ -367,14 +369,15 @@ namespace Avalonia.Controls Renderer?.Dispose(); Renderer = null; + (this as IInputRoot).MouseDevice?.TopLevelClosed(this); + PlatformImpl = null; + var logicalArgs = new LogicalTreeAttachmentEventArgs(this, this, null); ((ILogical)this).NotifyDetachedFromLogicalTree(logicalArgs); var visualArgs = new VisualTreeAttachmentEventArgs(this, this); OnDetachedFromVisualTreeCore(visualArgs); - - (this as IInputRoot).MouseDevice?.TopLevelClosed(this); - PlatformImpl = null; + OnClosed(EventArgs.Empty); LayoutManager?.Dispose(); diff --git a/src/Avalonia.Controls/ValidatingToplevel.cs b/src/Avalonia.Controls/ValidatingToplevel.cs new file mode 100644 index 0000000000..d1edd28ebc --- /dev/null +++ b/src/Avalonia.Controls/ValidatingToplevel.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls.Platform; +using Avalonia.Controls.Primitives.PopupPositioning; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Input.TextInput; +using Avalonia.Platform; +using Avalonia.Rendering; + +namespace Avalonia.Controls; + +internal class ValidatingToplevelImpl : ITopLevelImpl, ITopLevelImplWithNativeControlHost, + ITopLevelImplWithNativeMenuExporter, ITopLevelImplWithTextInputMethod +{ + private readonly ITopLevelImpl _impl; + private bool _disposed; + + public ValidatingToplevelImpl(ITopLevelImpl impl) + { + _impl = impl ?? throw new InvalidOperationException( + "Could not create TopLevel implementation: maybe no windowing subsystem was initialized?"); + } + + public void Dispose() + { + _disposed = true; + _impl.Dispose(); + } + + protected void CheckDisposed() + { + if (_disposed) + throw new ObjectDisposedException(_impl.GetType().FullName); + } + + protected ITopLevelImpl Inner + { + get + { + CheckDisposed(); + return _impl; + } + } + + public static ITopLevelImpl Wrap(ITopLevelImpl impl) + { +#if DEBUG + if (impl is ValidatingToplevelImpl) + return impl; + return new ValidatingToplevelImpl(impl); +#else + return impl; +#endif + } + + public Size ClientSize => Inner.ClientSize; + public Size? FrameSize => Inner.FrameSize; + public double RenderScaling => Inner.RenderScaling; + public IEnumerable Surfaces => Inner.Surfaces; + + public Action Input + { + get => Inner.Input; + set => Inner.Input = value; + } + + public Action Paint + { + get => Inner.Paint; + set => Inner.Paint = value; + } + + public Action Resized + { + get => Inner.Resized; + set => Inner.Resized = value; + } + + public Action ScalingChanged + { + get => Inner.ScalingChanged; + set => Inner.ScalingChanged = value; + } + + public Action TransparencyLevelChanged + { + get => Inner.TransparencyLevelChanged; + set => Inner.TransparencyLevelChanged = value; + } + + public IRenderer CreateRenderer(IRenderRoot root) => Inner.CreateRenderer(root); + + public void Invalidate(Rect rect) => Inner.Invalidate(rect); + + public void SetInputRoot(IInputRoot inputRoot) => Inner.SetInputRoot(inputRoot); + + public Point PointToClient(PixelPoint point) => Inner.PointToClient(point); + + public PixelPoint PointToScreen(Point point) => Inner.PointToScreen(point); + + public void SetCursor(ICursorImpl cursor) => Inner.SetCursor(cursor); + + public Action Closed + { + get => Inner.Closed; + set => Inner.Closed = value; + } + + public Action LostFocus + { + get => Inner.LostFocus; + set => Inner.LostFocus = value; + } + + // Exception: for some reason we are notifying platform mouse device from TopLevel.cs + public IMouseDevice MouseDevice => _impl.MouseDevice; + public IPopupImpl CreatePopup() => Inner.CreatePopup(); + + public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) => + Inner.SetTransparencyLevelHint(transparencyLevel); + + + public WindowTransparencyLevel TransparencyLevel => Inner.TransparencyLevel; + public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => Inner.AcrylicCompensationLevels; + public INativeControlHostImpl NativeControlHost => (Inner as ITopLevelImplWithNativeControlHost)?.NativeControlHost; + + public ITopLevelNativeMenuExporter NativeMenuExporter => + (Inner as ITopLevelImplWithNativeMenuExporter)?.NativeMenuExporter; + + public ITextInputMethodImpl TextInputMethod => (Inner as ITopLevelImplWithTextInputMethod)?.TextInputMethod; +} + +internal class ValidatingWindowBaseImpl : ValidatingToplevelImpl, IWindowBaseImpl +{ + private readonly IWindowBaseImpl _impl; + + public ValidatingWindowBaseImpl(IWindowBaseImpl impl) : base(impl) + { + _impl = impl; + } + + protected new IWindowBaseImpl Inner + { + get + { + CheckDisposed(); + return _impl; + } + } + + public static IWindowBaseImpl Wrap(IWindowBaseImpl impl) + { +#if DEBUG + if (impl is ValidatingToplevelImpl) + return impl; + return new ValidatingWindowBaseImpl(impl); +#else + return impl; +#endif + } + + public void Show(bool activate, bool isDialog) => Inner.Show(activate, isDialog); + + public void Hide() => Inner.Hide(); + + public double DesktopScaling => Inner.DesktopScaling; + public PixelPoint Position => Inner.Position; + + public Action PositionChanged + { + get => Inner.PositionChanged; + set => Inner.PositionChanged = value; + } + + public void Activate() => Inner.Activate(); + + public Action Deactivated + { + get => Inner.Deactivated; + set => Inner.Deactivated = value; + } + + public Action Activated + { + get => Inner.Activated; + set => Inner.Deactivated = value; + } + + public IPlatformHandle Handle => Inner.Handle; + public Size MaxAutoSizeHint => Inner.MaxAutoSizeHint; + public void SetTopmost(bool value) => Inner.SetTopmost(value); + public IScreenImpl Screen => Inner.Screen; +} + +internal class ValidatingWindowImpl : ValidatingWindowBaseImpl, IWindowImpl +{ + private readonly IWindowImpl _impl; + + public ValidatingWindowImpl(IWindowImpl impl) : base(impl) + { + _impl = impl; + } + + protected new IWindowImpl Inner + { + get + { + CheckDisposed(); + return _impl; + } + } + + public static IWindowImpl Wrap(IWindowImpl impl) + { +#if DEBUG + if (impl is ValidatingToplevelImpl) + return impl; + return new ValidatingWindowImpl(impl); +#else + return impl; +#endif + } + + public WindowState WindowState + { + get => Inner.WindowState; + set => Inner.WindowState = value; + } + + public Action WindowStateChanged + { + get => Inner.WindowStateChanged; + set => Inner.WindowStateChanged = value; + } + + public void SetTitle(string title) => Inner.SetTitle(title); + + public void SetParent(IWindowImpl parent) + { + //Workaround. SetParent will cast IWindowImpl to WindowImpl but ValidatingWindowImpl isn't actual WindowImpl so it will fail with InvalidCastException. + if (parent is ValidatingWindowImpl validatingToplevelImpl) + { + Inner.SetParent(validatingToplevelImpl.Inner); + } + else + { + Inner.SetParent(parent); + } + } + + public void SetEnabled(bool enable) => Inner.SetEnabled(enable); + + public Action GotInputWhenDisabled + { + get => Inner.GotInputWhenDisabled; + set => Inner.GotInputWhenDisabled = value; + } + + public void SetSystemDecorations(SystemDecorations enabled) => Inner.SetSystemDecorations(enabled); + + public void SetIcon(IWindowIconImpl icon) => Inner.SetIcon(icon); + + public void ShowTaskbarIcon(bool value) => Inner.ShowTaskbarIcon(value); + + public void CanResize(bool value) => Inner.CanResize(value); + + public Func Closing + { + get => Inner.Closing; + set => Inner.Closing = value; + } + + public bool IsClientAreaExtendedToDecorations => Inner.IsClientAreaExtendedToDecorations; + + public Action ExtendClientAreaToDecorationsChanged + { + get => Inner.ExtendClientAreaToDecorationsChanged; + set => Inner.ExtendClientAreaToDecorationsChanged = value; + } + + public bool NeedsManagedDecorations => Inner.NeedsManagedDecorations; + public Thickness ExtendedMargins => Inner.ExtendedMargins; + public Thickness OffScreenMargin => Inner.OffScreenMargin; + public void BeginMoveDrag(PointerPressedEventArgs e) => Inner.BeginMoveDrag(e); + + public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) => Inner.BeginResizeDrag(edge, e); + + public void Resize(Size clientSize, PlatformResizeReason reason) => + Inner.Resize(clientSize, reason); + + public void Move(PixelPoint point) => Inner.Move(point); + + public void SetMinMaxSize(Size minSize, Size maxSize) => Inner.SetMinMaxSize(minSize, maxSize); + + public void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint) => + Inner.SetExtendClientAreaToDecorationsHint(extendIntoClientAreaHint); + + public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints) => + Inner.SetExtendClientAreaChromeHints(hints); + + public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight) => + Inner.SetExtendClientAreaTitleBarHeightHint(titleBarHeight); +} + +internal class ValidatingPopupImpl : ValidatingWindowBaseImpl, IPopupImpl +{ + private readonly IPopupImpl _impl; + + public ValidatingPopupImpl(IPopupImpl impl) : base(impl) + { + _impl = impl; + } + + protected new IPopupImpl Inner + { + get + { + CheckDisposed(); + return _impl; + } + } + + public static IPopupImpl Wrap(IPopupImpl impl) + { +#if DEBUG + if (impl is ValidatingToplevelImpl) + return impl; + return new ValidatingPopupImpl(impl); +#else + return impl; +#endif + } + + public IPopupPositioner PopupPositioner => Inner.PopupPositioner; + public void SetWindowManagerAddShadowHint(bool enabled) => Inner.SetWindowManagerAddShadowHint(enabled); +} diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 4c94b725ea..b9ca594efa 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -237,13 +237,14 @@ namespace Avalonia.Controls /// /// The window implementation. public Window(IWindowImpl impl) - : base(impl) + : base(ValidatingWindowImpl.Wrap(impl)) { - impl.Closing = HandleClosing; - impl.GotInputWhenDisabled = OnGotInputWhenDisabled; - impl.WindowStateChanged = HandleWindowStateChanged; + var wrapped = (IWindowImpl)base.PlatformImpl!; + wrapped.Closing = HandleClosing; + wrapped.GotInputWhenDisabled = OnGotInputWhenDisabled; + wrapped.WindowStateChanged = HandleWindowStateChanged; _maxPlatformClientSize = PlatformImpl?.MaxAutoSizeHint ?? default(Size); - impl.ExtendClientAreaToDecorationsChanged = ExtendClientAreaToDecorationsChanged; + wrapped.ExtendClientAreaToDecorationsChanged = ExtendClientAreaToDecorationsChanged; this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x, PlatformResizeReason.Application)); PlatformImpl?.ShowTaskbarIcon(ShowInTaskbar); diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 5861d0452d..2207d0550a 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -57,12 +57,13 @@ namespace Avalonia.Controls { } - public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) + public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(ValidatingWindowBaseImpl.Wrap(impl), dependencyResolver) { Screens = new Screens(PlatformImpl?.Screen); - impl.Activated = HandleActivated; - impl.Deactivated = HandleDeactivated; - impl.PositionChanged = HandlePositionChanged; + var wrapped = PlatformImpl!; + wrapped.Activated = HandleActivated; + wrapped.Deactivated = HandleDeactivated; + wrapped.PositionChanged = HandlePositionChanged; } /// diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml index 45bfe5ff81..c32638f6ca 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views="clr-namespace:Avalonia.Diagnostics.Views" xmlns:diag="clr-namespace:Avalonia.Diagnostics" + xmlns:default="using:Avalonia.Themes.Default" Title="Avalonia DevTools" x:Class="Avalonia.Diagnostics.Views.MainWindow"> @@ -9,8 +10,7 @@ - - + diff --git a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml index 5f18bac44a..1843abebfd 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseDark.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseDark.xaml @@ -1,13 +1,9 @@ - diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml index 30c6d39856..6247815303 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml @@ -1,82 +1,38 @@ - diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index 678f75b43f..ef200b5532 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -13,10 +13,7 @@ - - - - + diff --git a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml b/src/Avalonia.Themes.Default/Controls/AutoCompleteBox.xaml similarity index 100% rename from src/Avalonia.Themes.Default/AutoCompleteBox.xaml rename to src/Avalonia.Themes.Default/Controls/AutoCompleteBox.xaml diff --git a/src/Avalonia.Themes.Default/Button.xaml b/src/Avalonia.Themes.Default/Controls/Button.xaml similarity index 100% rename from src/Avalonia.Themes.Default/Button.xaml rename to src/Avalonia.Themes.Default/Controls/Button.xaml diff --git a/src/Avalonia.Themes.Default/ButtonSpinner.xaml b/src/Avalonia.Themes.Default/Controls/ButtonSpinner.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ButtonSpinner.xaml rename to src/Avalonia.Themes.Default/Controls/ButtonSpinner.xaml diff --git a/src/Avalonia.Themes.Default/Calendar.xaml b/src/Avalonia.Themes.Default/Controls/Calendar.xaml similarity index 100% rename from src/Avalonia.Themes.Default/Calendar.xaml rename to src/Avalonia.Themes.Default/Controls/Calendar.xaml diff --git a/src/Avalonia.Themes.Default/CalendarButton.xaml b/src/Avalonia.Themes.Default/Controls/CalendarButton.xaml similarity index 100% rename from src/Avalonia.Themes.Default/CalendarButton.xaml rename to src/Avalonia.Themes.Default/Controls/CalendarButton.xaml diff --git a/src/Avalonia.Themes.Default/CalendarDatePicker.xaml b/src/Avalonia.Themes.Default/Controls/CalendarDatePicker.xaml similarity index 100% rename from src/Avalonia.Themes.Default/CalendarDatePicker.xaml rename to src/Avalonia.Themes.Default/Controls/CalendarDatePicker.xaml diff --git a/src/Avalonia.Themes.Default/CalendarDayButton.xaml b/src/Avalonia.Themes.Default/Controls/CalendarDayButton.xaml similarity index 100% rename from src/Avalonia.Themes.Default/CalendarDayButton.xaml rename to src/Avalonia.Themes.Default/Controls/CalendarDayButton.xaml diff --git a/src/Avalonia.Themes.Default/CalendarItem.xaml b/src/Avalonia.Themes.Default/Controls/CalendarItem.xaml similarity index 100% rename from src/Avalonia.Themes.Default/CalendarItem.xaml rename to src/Avalonia.Themes.Default/Controls/CalendarItem.xaml diff --git a/src/Avalonia.Themes.Default/CaptionButtons.xaml b/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml similarity index 100% rename from src/Avalonia.Themes.Default/CaptionButtons.xaml rename to src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml diff --git a/src/Avalonia.Themes.Default/Carousel.xaml b/src/Avalonia.Themes.Default/Controls/Carousel.xaml similarity index 100% rename from src/Avalonia.Themes.Default/Carousel.xaml rename to src/Avalonia.Themes.Default/Controls/Carousel.xaml diff --git a/src/Avalonia.Themes.Default/CheckBox.xaml b/src/Avalonia.Themes.Default/Controls/CheckBox.xaml similarity index 100% rename from src/Avalonia.Themes.Default/CheckBox.xaml rename to src/Avalonia.Themes.Default/Controls/CheckBox.xaml diff --git a/src/Avalonia.Themes.Default/ComboBox.xaml b/src/Avalonia.Themes.Default/Controls/ComboBox.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ComboBox.xaml rename to src/Avalonia.Themes.Default/Controls/ComboBox.xaml diff --git a/src/Avalonia.Themes.Default/ComboBoxItem.xaml b/src/Avalonia.Themes.Default/Controls/ComboBoxItem.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ComboBoxItem.xaml rename to src/Avalonia.Themes.Default/Controls/ComboBoxItem.xaml diff --git a/src/Avalonia.Themes.Default/ContentControl.xaml b/src/Avalonia.Themes.Default/Controls/ContentControl.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ContentControl.xaml rename to src/Avalonia.Themes.Default/Controls/ContentControl.xaml diff --git a/src/Avalonia.Themes.Default/ContextMenu.xaml b/src/Avalonia.Themes.Default/Controls/ContextMenu.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ContextMenu.xaml rename to src/Avalonia.Themes.Default/Controls/ContextMenu.xaml diff --git a/src/Avalonia.Themes.Default/DataValidationErrors.xaml b/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml similarity index 100% rename from src/Avalonia.Themes.Default/DataValidationErrors.xaml rename to src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml diff --git a/src/Avalonia.Themes.Default/DatePicker.xaml b/src/Avalonia.Themes.Default/Controls/DatePicker.xaml similarity index 100% rename from src/Avalonia.Themes.Default/DatePicker.xaml rename to src/Avalonia.Themes.Default/Controls/DatePicker.xaml diff --git a/src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml b/src/Avalonia.Themes.Default/Controls/EmbeddableControlRoot.xaml similarity index 100% rename from src/Avalonia.Themes.Default/EmbeddableControlRoot.xaml rename to src/Avalonia.Themes.Default/Controls/EmbeddableControlRoot.xaml diff --git a/src/Avalonia.Themes.Default/Expander.xaml b/src/Avalonia.Themes.Default/Controls/Expander.xaml similarity index 100% rename from src/Avalonia.Themes.Default/Expander.xaml rename to src/Avalonia.Themes.Default/Controls/Expander.xaml diff --git a/src/Avalonia.Themes.Default/FlyoutPresenter.xaml b/src/Avalonia.Themes.Default/Controls/FlyoutPresenter.xaml similarity index 100% rename from src/Avalonia.Themes.Default/FlyoutPresenter.xaml rename to src/Avalonia.Themes.Default/Controls/FlyoutPresenter.xaml diff --git a/src/Avalonia.Themes.Default/FocusAdorner.xaml b/src/Avalonia.Themes.Default/Controls/FocusAdorner.xaml similarity index 100% rename from src/Avalonia.Themes.Default/FocusAdorner.xaml rename to src/Avalonia.Themes.Default/Controls/FocusAdorner.xaml diff --git a/src/Avalonia.Themes.Default/GridSplitter.xaml b/src/Avalonia.Themes.Default/Controls/GridSplitter.xaml similarity index 100% rename from src/Avalonia.Themes.Default/GridSplitter.xaml rename to src/Avalonia.Themes.Default/Controls/GridSplitter.xaml diff --git a/src/Avalonia.Themes.Default/ItemsControl.xaml b/src/Avalonia.Themes.Default/Controls/ItemsControl.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ItemsControl.xaml rename to src/Avalonia.Themes.Default/Controls/ItemsControl.xaml diff --git a/src/Avalonia.Themes.Default/Label.xaml b/src/Avalonia.Themes.Default/Controls/Label.xaml similarity index 100% rename from src/Avalonia.Themes.Default/Label.xaml rename to src/Avalonia.Themes.Default/Controls/Label.xaml diff --git a/src/Avalonia.Themes.Default/ListBox.xaml b/src/Avalonia.Themes.Default/Controls/ListBox.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ListBox.xaml rename to src/Avalonia.Themes.Default/Controls/ListBox.xaml diff --git a/src/Avalonia.Themes.Default/ListBoxItem.xaml b/src/Avalonia.Themes.Default/Controls/ListBoxItem.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ListBoxItem.xaml rename to src/Avalonia.Themes.Default/Controls/ListBoxItem.xaml diff --git a/src/Avalonia.Themes.Default/ManagedFileChooser.xaml b/src/Avalonia.Themes.Default/Controls/ManagedFileChooser.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ManagedFileChooser.xaml rename to src/Avalonia.Themes.Default/Controls/ManagedFileChooser.xaml diff --git a/src/Avalonia.Themes.Default/Menu.xaml b/src/Avalonia.Themes.Default/Controls/Menu.xaml similarity index 100% rename from src/Avalonia.Themes.Default/Menu.xaml rename to src/Avalonia.Themes.Default/Controls/Menu.xaml diff --git a/src/Avalonia.Themes.Default/MenuFlyoutPresenter.xaml b/src/Avalonia.Themes.Default/Controls/MenuFlyoutPresenter.xaml similarity index 100% rename from src/Avalonia.Themes.Default/MenuFlyoutPresenter.xaml rename to src/Avalonia.Themes.Default/Controls/MenuFlyoutPresenter.xaml diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/Controls/MenuItem.xaml similarity index 100% rename from src/Avalonia.Themes.Default/MenuItem.xaml rename to src/Avalonia.Themes.Default/Controls/MenuItem.xaml diff --git a/src/Avalonia.Themes.Default/NativeMenuBar.xaml b/src/Avalonia.Themes.Default/Controls/NativeMenuBar.xaml similarity index 100% rename from src/Avalonia.Themes.Default/NativeMenuBar.xaml rename to src/Avalonia.Themes.Default/Controls/NativeMenuBar.xaml diff --git a/src/Avalonia.Themes.Default/NotificationCard.xaml b/src/Avalonia.Themes.Default/Controls/NotificationCard.xaml similarity index 100% rename from src/Avalonia.Themes.Default/NotificationCard.xaml rename to src/Avalonia.Themes.Default/Controls/NotificationCard.xaml diff --git a/src/Avalonia.Themes.Default/NumericUpDown.xaml b/src/Avalonia.Themes.Default/Controls/NumericUpDown.xaml similarity index 100% rename from src/Avalonia.Themes.Default/NumericUpDown.xaml rename to src/Avalonia.Themes.Default/Controls/NumericUpDown.xaml diff --git a/src/Avalonia.Themes.Default/OverlayPopupHost.xaml b/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml similarity index 100% rename from src/Avalonia.Themes.Default/OverlayPopupHost.xaml rename to src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml diff --git a/src/Avalonia.Themes.Default/PathIcon.xaml b/src/Avalonia.Themes.Default/Controls/PathIcon.xaml similarity index 100% rename from src/Avalonia.Themes.Default/PathIcon.xaml rename to src/Avalonia.Themes.Default/Controls/PathIcon.xaml diff --git a/src/Avalonia.Themes.Default/PopupRoot.xaml b/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml similarity index 100% rename from src/Avalonia.Themes.Default/PopupRoot.xaml rename to src/Avalonia.Themes.Default/Controls/PopupRoot.xaml diff --git a/src/Avalonia.Themes.Default/ProgressBar.xaml b/src/Avalonia.Themes.Default/Controls/ProgressBar.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ProgressBar.xaml rename to src/Avalonia.Themes.Default/Controls/ProgressBar.xaml diff --git a/src/Avalonia.Themes.Default/RadioButton.xaml b/src/Avalonia.Themes.Default/Controls/RadioButton.xaml similarity index 100% rename from src/Avalonia.Themes.Default/RadioButton.xaml rename to src/Avalonia.Themes.Default/Controls/RadioButton.xaml diff --git a/src/Avalonia.Themes.Default/RepeatButton.xaml b/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml similarity index 100% rename from src/Avalonia.Themes.Default/RepeatButton.xaml rename to src/Avalonia.Themes.Default/Controls/RepeatButton.xaml diff --git a/src/Avalonia.Themes.Default/ScrollBar.xaml b/src/Avalonia.Themes.Default/Controls/ScrollBar.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ScrollBar.xaml rename to src/Avalonia.Themes.Default/Controls/ScrollBar.xaml diff --git a/src/Avalonia.Themes.Default/ScrollViewer.xaml b/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ScrollViewer.xaml rename to src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml diff --git a/src/Avalonia.Themes.Default/Separator.xaml b/src/Avalonia.Themes.Default/Controls/Separator.xaml similarity index 100% rename from src/Avalonia.Themes.Default/Separator.xaml rename to src/Avalonia.Themes.Default/Controls/Separator.xaml diff --git a/src/Avalonia.Themes.Default/Slider.xaml b/src/Avalonia.Themes.Default/Controls/Slider.xaml similarity index 100% rename from src/Avalonia.Themes.Default/Slider.xaml rename to src/Avalonia.Themes.Default/Controls/Slider.xaml diff --git a/src/Avalonia.Themes.Default/SplitView.xaml b/src/Avalonia.Themes.Default/Controls/SplitView.xaml similarity index 100% rename from src/Avalonia.Themes.Default/SplitView.xaml rename to src/Avalonia.Themes.Default/Controls/SplitView.xaml diff --git a/src/Avalonia.Themes.Default/TabControl.xaml b/src/Avalonia.Themes.Default/Controls/TabControl.xaml similarity index 100% rename from src/Avalonia.Themes.Default/TabControl.xaml rename to src/Avalonia.Themes.Default/Controls/TabControl.xaml diff --git a/src/Avalonia.Themes.Default/TabItem.xaml b/src/Avalonia.Themes.Default/Controls/TabItem.xaml similarity index 100% rename from src/Avalonia.Themes.Default/TabItem.xaml rename to src/Avalonia.Themes.Default/Controls/TabItem.xaml diff --git a/src/Avalonia.Themes.Default/TabStrip.xaml b/src/Avalonia.Themes.Default/Controls/TabStrip.xaml similarity index 100% rename from src/Avalonia.Themes.Default/TabStrip.xaml rename to src/Avalonia.Themes.Default/Controls/TabStrip.xaml diff --git a/src/Avalonia.Themes.Default/TabStripItem.xaml b/src/Avalonia.Themes.Default/Controls/TabStripItem.xaml similarity index 100% rename from src/Avalonia.Themes.Default/TabStripItem.xaml rename to src/Avalonia.Themes.Default/Controls/TabStripItem.xaml diff --git a/src/Avalonia.Themes.Default/TextBox.xaml b/src/Avalonia.Themes.Default/Controls/TextBox.xaml similarity index 100% rename from src/Avalonia.Themes.Default/TextBox.xaml rename to src/Avalonia.Themes.Default/Controls/TextBox.xaml diff --git a/src/Avalonia.Themes.Default/TimePicker.xaml b/src/Avalonia.Themes.Default/Controls/TimePicker.xaml similarity index 100% rename from src/Avalonia.Themes.Default/TimePicker.xaml rename to src/Avalonia.Themes.Default/Controls/TimePicker.xaml diff --git a/src/Avalonia.Themes.Default/TitleBar.xaml b/src/Avalonia.Themes.Default/Controls/TitleBar.xaml similarity index 100% rename from src/Avalonia.Themes.Default/TitleBar.xaml rename to src/Avalonia.Themes.Default/Controls/TitleBar.xaml diff --git a/src/Avalonia.Themes.Default/ToggleButton.xaml b/src/Avalonia.Themes.Default/Controls/ToggleButton.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ToggleButton.xaml rename to src/Avalonia.Themes.Default/Controls/ToggleButton.xaml diff --git a/src/Avalonia.Themes.Default/ToggleSwitch.xaml b/src/Avalonia.Themes.Default/Controls/ToggleSwitch.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ToggleSwitch.xaml rename to src/Avalonia.Themes.Default/Controls/ToggleSwitch.xaml diff --git a/src/Avalonia.Themes.Default/ToolTip.xaml b/src/Avalonia.Themes.Default/Controls/ToolTip.xaml similarity index 100% rename from src/Avalonia.Themes.Default/ToolTip.xaml rename to src/Avalonia.Themes.Default/Controls/ToolTip.xaml diff --git a/src/Avalonia.Themes.Default/TreeView.xaml b/src/Avalonia.Themes.Default/Controls/TreeView.xaml similarity index 100% rename from src/Avalonia.Themes.Default/TreeView.xaml rename to src/Avalonia.Themes.Default/Controls/TreeView.xaml diff --git a/src/Avalonia.Themes.Default/TreeViewItem.xaml b/src/Avalonia.Themes.Default/Controls/TreeViewItem.xaml similarity index 100% rename from src/Avalonia.Themes.Default/TreeViewItem.xaml rename to src/Avalonia.Themes.Default/Controls/TreeViewItem.xaml diff --git a/src/Avalonia.Themes.Default/UserControl.xaml b/src/Avalonia.Themes.Default/Controls/UserControl.xaml similarity index 100% rename from src/Avalonia.Themes.Default/UserControl.xaml rename to src/Avalonia.Themes.Default/Controls/UserControl.xaml diff --git a/src/Avalonia.Themes.Default/Window.xaml b/src/Avalonia.Themes.Default/Controls/Window.xaml similarity index 100% rename from src/Avalonia.Themes.Default/Window.xaml rename to src/Avalonia.Themes.Default/Controls/Window.xaml diff --git a/src/Avalonia.Themes.Default/WindowNotificationManager.xaml b/src/Avalonia.Themes.Default/Controls/WindowNotificationManager.xaml similarity index 100% rename from src/Avalonia.Themes.Default/WindowNotificationManager.xaml rename to src/Avalonia.Themes.Default/Controls/WindowNotificationManager.xaml diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml index 109fde390c..702fcc7c7a 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml @@ -2,65 +2,65 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Avalonia.Themes.Default.DefaultTheme"> - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs b/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs index effdb88537..598b418977 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml.cs @@ -1,4 +1,3 @@ -using Avalonia.Markup.Xaml; using Avalonia.Styling; namespace Avalonia.Themes.Default diff --git a/src/Avalonia.Themes.Default/SimpleTheme.cs b/src/Avalonia.Themes.Default/SimpleTheme.cs new file mode 100644 index 0000000000..1d9f2d5f9d --- /dev/null +++ b/src/Avalonia.Themes.Default/SimpleTheme.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml.Styling; +using Avalonia.Styling; +#nullable enable + +namespace Avalonia.Themes.Default +{ + public class SimpleTheme : AvaloniaObject, IStyle, IResourceProvider + { + public static readonly StyledProperty ModeProperty = + AvaloniaProperty.Register(nameof(Mode)); + + private readonly Uri _baseUri; + private bool _isLoading; + private IStyle? _loaded; + private Styles _sharedStyles = new(); + private Styles _simpleDark = new(); + private Styles _simpleLight = new(); + /// + /// Initializes a new instance of the class. + /// + /// The base URL for the XAML context. + public SimpleTheme(Uri baseUri) + { + _baseUri = baseUri; + InitStyles(_baseUri); + } + + /// + /// Initializes a new instance of the class. + /// + /// The XAML service provider. + public SimpleTheme(IServiceProvider serviceProvider) + { + var service = serviceProvider.GetService(typeof(IUriContext)); + if (service == null) + { + throw new Exception("There is no service object of type IUriContext!"); + } + _baseUri = ((IUriContext)service).BaseUri; + InitStyles(_baseUri); + } + + public event EventHandler OwnerChanged + { + add + { + if (Loaded is IResourceProvider rp) + { + rp.OwnerChanged += value; + } + } + remove + { + if (Loaded is IResourceProvider rp) + { + rp.OwnerChanged -= value; + } + } + } + + IReadOnlyList IStyle.Children => _loaded?.Children ?? Array.Empty(); + + bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false; + + public IStyle Loaded + { + get + { + if (_loaded == null) + { + _isLoading = true; + + if (Mode == SimpleThemeMode.Light) + { + _loaded = new Styles { _sharedStyles, _simpleLight }; + } + else if (Mode == SimpleThemeMode.Dark) + { + _loaded = new Styles { _sharedStyles, _simpleDark }; + } + _isLoading = false; + } + + return _loaded!; + } + } + + /// + /// Gets or sets the mode of the fluent theme (light, dark). + /// + public SimpleThemeMode Mode + { + get => GetValue(ModeProperty); + set => SetValue(ModeProperty, value); + } + public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner; + + void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner); + + void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); + + public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) => Loaded.TryAttach(target, host); + + public bool TryGetResource(object key, out object? value) + { + if (!_isLoading && Loaded is IResourceProvider p) + { + return p.TryGetResource(key, out value); + } + + value = null; + return false; + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == ModeProperty) + { + if (Mode == SimpleThemeMode.Dark) + { + (Loaded as Styles)![1] = _simpleDark[0]; + } + else + { + (Loaded as Styles)![1] = _simpleLight[0]; + } + } + } + + private void InitStyles(Uri baseUri) + { + _sharedStyles = new Styles + { + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml") + }, + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Default/Accents/Base.xaml") + } + }; + _simpleLight = new Styles + { + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml") + } + }; + + _simpleDark = new Styles + { + new StyleInclude(baseUri) + { + Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseDark.xaml") + } + }; + } + + } +} diff --git a/src/Avalonia.Themes.Default/SimpleThemeMode.cs b/src/Avalonia.Themes.Default/SimpleThemeMode.cs new file mode 100644 index 0000000000..be33466327 --- /dev/null +++ b/src/Avalonia.Themes.Default/SimpleThemeMode.cs @@ -0,0 +1,8 @@ +namespace Avalonia.Themes.Default +{ + public enum SimpleThemeMode + { + Light, + Dark + } +} diff --git a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml index f2344ab380..836cc27db3 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml @@ -1,7 +1,8 @@ + xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls" + x:CompileBindings="True"> diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml index ca538e4b0a..3a6af60983 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml @@ -11,7 +11,7 @@ - + diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index 26c3bbc19f..ffd3972b66 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -7,7 +7,8 @@ + xmlns:sys="clr-namespace:System;assembly=netstandard" + x:CompileBindings="True"> - + diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml index 5bf3ac11af..a9c8281cf0 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml @@ -6,7 +6,9 @@ --> + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + x:CompileBindings="True" + x:DataType="CalendarItem"> @@ -32,6 +34,7 @@ - \ No newline at end of file + diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml index 72c25cea37..831537f578 100644 --- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml @@ -1,7 +1,9 @@ + xmlns:sys="clr-namespace:System;assembly=netstandard" + x:DataType="MenuItem" + x:CompileBindings="True"> diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml index 799fe6ffe4..243095c004 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml @@ -1,6 +1,7 @@ diff --git a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml index 3320fc9a41..9aa73fc52e 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml @@ -7,7 +7,8 @@ + xmlns:sys="clr-namespace:System;assembly=netstandard" + x:CompileBindings="True"> 40 1 diff --git a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml index debdfb2772..2d18be91cb 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml @@ -1,6 +1,7 @@ + xmlns:sys="clr-namespace:System;assembly=netstandard" + x:CompileBindings="True"> + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + x:DataType="WindowNotificationManager" + x:CompileBindings="True">