Browse Source

Merge branch 'master' into fix-win32-dialog-minimizing-parent

pull/4928/head
Dariusz Komosiński 6 years ago
committed by GitHub
parent
commit
867820303c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      samples/ControlCatalog/App.xaml
  2. 1
      samples/ControlCatalog/MainView.xaml
  3. 42
      samples/ControlCatalog/Pages/LabelsPage.axaml
  4. 43
      samples/ControlCatalog/Pages/LabelsPage.axaml.cs
  5. 36
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  6. 74
      src/Avalonia.Controls/Label.cs
  7. 25
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  8. 28
      src/Avalonia.Controls/Templates/FuncDataTemplate.cs
  9. 3
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  10. 17
      src/Avalonia.Themes.Default/Label.xaml
  11. 3
      src/Avalonia.Themes.Fluent/FluentTheme.xaml
  12. 17
      src/Avalonia.Themes.Fluent/Label.xaml
  13. 176
      src/Windows/Avalonia.Win32/FramebufferManager.cs
  14. 100
      src/Windows/Avalonia.Win32/WindowFramebuffer.cs

10
samples/ControlCatalog/App.xaml

@ -12,6 +12,16 @@
<Style Selector="TextBlock.h3"> <Style Selector="TextBlock.h3">
<Setter Property="FontSize" Value="12" /> <Setter Property="FontSize" Value="12" />
</Style> </Style>
<Style Selector="Label.h1">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Medium" />
</Style>
<Style Selector="Label.h2">
<Setter Property="FontSize" Value="14" />
</Style>
<Style Selector="Label.h3">
<Setter Property="FontSize" Value="12" />
</Style>
<StyleInclude Source="/SideBar.xaml"/> <StyleInclude Source="/SideBar.xaml"/>
</Application.Styles> </Application.Styles>
</Application> </Application>

1
samples/ControlCatalog/MainView.xaml

@ -14,6 +14,7 @@
<TabItem Header="Acrylic"><pages:AcrylicPage/></TabItem> <TabItem Header="Acrylic"><pages:AcrylicPage/></TabItem>
<TabItem Header="AutoCompleteBox"><pages:AutoCompleteBoxPage/></TabItem> <TabItem Header="AutoCompleteBox"><pages:AutoCompleteBoxPage/></TabItem>
<TabItem Header="Border"><pages:BorderPage/></TabItem> <TabItem Header="Border"><pages:BorderPage/></TabItem>
<TabItem Header="Label"><pages:LabelsPage/></TabItem>
<TabItem Header="Button"><pages:ButtonPage/></TabItem> <TabItem Header="Button"><pages:ButtonPage/></TabItem>
<TabItem Header="ButtonSpinner"><pages:ButtonSpinnerPage/></TabItem> <TabItem Header="ButtonSpinner"><pages:ButtonSpinnerPage/></TabItem>
<TabItem Header="Calendar"><pages:CalendarPage/></TabItem> <TabItem Header="Calendar"><pages:CalendarPage/></TabItem>

42
samples/ControlCatalog/Pages/LabelsPage.axaml

@ -0,0 +1,42 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="250"
x:Class="ControlCatalog.Pages.LabelsPage"
x:Name="_labelsPage">
<UserControl.Styles>
<Style Selector="Label">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="6,3,0,3"/>
</Style>
<Style Selector="TextBox">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,3,6,3"/>
</Style>
<Style Selector="CheckBox">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,3,6,3"/>
</Style>
<Style Selector="Button[IsDefault=true]">
<Setter Property="Background" Value="{DynamicResource HighlightBrush}"/>
</Style>
</UserControl.Styles>
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Hidden">
<Grid HorizontalAlignment="Left" VerticalAlignment="Top" RowDefinitions="Auto,Auto,Auto,Auto,Auto,*" ColumnDefinitions="Auto,6,*" Width="246">
<Label Target="{Binding #firstNameEdit}" Grid.Row="0" Grid.Column="0">_First name</Label>
<TextBox Name="firstNameEdit" Grid.Column="2" Grid.Row="0" Text="{Binding FirstName}"></TextBox>
<Label Target="{Binding #lastNameEdit}" Grid.Row="1" Grid.Column="0">_Last name</Label>
<TextBox Name="lastNameEdit" Grid.Column="2" Grid.Row="1" Text="{Binding LastName}"></TextBox>
<Label Target="{Binding #bannedCheck}" Grid.Row="2" Grid.Column="0">_Banned</Label>
<CheckBox Name="bannedCheck" Grid.Column="2" Grid.Row="2" IsChecked="{Binding IsBanned}"></CheckBox>
<GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.RowSpan="3" >
</GridSplitter>
<StackPanel Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="3" Orientation="Horizontal" HorizontalAlignment="Right">
<Button IsCancel="True" Command="{Binding #_labelsPage.DoCancel}">Cancel</Button>
<Button IsDefault="True" Command="{Binding #_labelsPage.DoSave}">Save</Button>
</StackPanel>
</Grid>
</ScrollViewer>
</UserControl>

43
samples/ControlCatalog/Pages/LabelsPage.axaml.cs

@ -0,0 +1,43 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.Models;
using ReactiveUI;
namespace ControlCatalog.Pages
{
public class LabelsPage : UserControl
{
private Person _person;
public LabelsPage()
{
CreateDefaultPerson();
this.InitializeComponent();
}
private void CreateDefaultPerson()
{
DataContext = _person = new Person
{
FirstName = "John",
LastName = "Doe",
IsBanned = true,
};
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void DoSave()
{
}
public void DoCancel()
{
CreateDefaultPerson();
}
}
}

36
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -2,8 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.TextBoxPage"> x:Class="ControlCatalog.Pages.TextBoxPage">
<StackPanel Orientation="Vertical" Spacing="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">TextBox</TextBlock> <Label Classes="h1">TextBox</Label>
<TextBlock Classes="h2">A control into which the user can input text</TextBlock> <Label Classes="h2">A control into which the user can input text</Label>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
@ -38,24 +38,24 @@
Text="Multiline TextBox with TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." /> Text="Multiline TextBox with TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
<TextBox AcceptsReturn="True" Width="200" Height="125" <TextBox AcceptsReturn="True" Width="200" Height="125"
Text="Multiline TextBox with no TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." /> Text="Multiline TextBox with no TextWrapping.&#xD;&#xD;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est." />
<TextBox Classes="clearButton" Text="Clear Content" Width="200" FontWeight="Normal" FontStyle="Normal" Watermark="Watermark" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/> <TextBox Classes="clearButton" Text="Clear Content" Width="200" FontWeight="Normal" FontStyle="Normal" Watermark="Watermark" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" Spacing="8"> <StackPanel Orientation="Vertical" Spacing="8">
<TextBlock Classes="h2">resm fonts</TextBlock> <Label Classes="h2" Target="{Binding #firstResMFont}">res_m fonts</Label>
<TextBox Width="200" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/> <TextBox Width="200" x:Name="firstResMFont" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/> <TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="resm:ControlCatalog.Assets.Fonts?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-Italic.ttf?assembly=ControlCatalog#Source Sans Pro"/> <TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-Italic.ttf?assembly=ControlCatalog#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-*.ttf?assembly=ControlCatalog#Source Sans Pro"/> <TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="resm:ControlCatalog.Assets.Fonts.SourceSansPro-*.ttf?assembly=ControlCatalog#Source Sans Pro"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" Spacing="8"> <StackPanel Orientation="Vertical" Spacing="8">
<TextBlock Classes="h2">res fonts</TextBlock> <Label Classes="h2" Target="{Binding #firstResFont}">_res fonts</Label>
<TextBox Width="200" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/> <TextBox Width="200" x:Name="firstResFont" Text="Custom font regular" FontWeight="Normal" FontStyle="Normal" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/> <TextBox Width="200" Text="Custom font bold" FontWeight="Bold" FontStyle="Normal" FontFamily="avares://ControlCatalog/Assets/Fonts#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="/Assets/Fonts/SourceSansPro-Italic.ttf#Source Sans Pro"/> <TextBox Width="200" Text="Custom font italic" FontWeight="Normal" FontStyle="Italic" FontFamily="/Assets/Fonts/SourceSansPro-Italic.ttf#Source Sans Pro"/>
<TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="/Assets/Fonts/SourceSansPro-*.ttf#Source Sans Pro"/> <TextBox Width="200" Text="Custom font italic bold" FontWeight="Bold" FontStyle="Italic" FontFamily="/Assets/Fonts/SourceSansPro-*.ttf#Source Sans Pro"/>
</StackPanel>
</StackPanel> </StackPanel>
</StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

74
src/Avalonia.Controls/Label.cs

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
namespace Avalonia.Controls
{
/// <summary>
/// Label control. Focuses <see cref="Target"/> on pointer click or access key press (Alt + accessKey)
/// </summary>
public class Label : ContentControl
{
/// <summary>
/// Defines the <see cref="Target"/> Direct property
/// </summary>
public static readonly DirectProperty<Label, IInputElement> TargetProperty =
AvaloniaProperty.RegisterDirect<Label, IInputElement>(nameof(Target), lbl => lbl.Target, (lbl, inp) => lbl.Target = inp);
/// <summary>
/// Label focus target storage field
/// </summary>
private IInputElement _target;
/// <summary>
/// Label focus Target
/// </summary>
public IInputElement Target
{
get => _target;
set => SetAndRaise(TargetProperty, ref _target, value);
}
static Label()
{
AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler<Label>((lbl, args) => lbl.LabelActivated(args));
// IsTabStopProperty.OverrideDefaultValue<Label>(false)
FocusableProperty.OverrideDefaultValue<Label>(false);
}
/// <summary>
/// Initializes instance of <see cref="Label"/> control
/// </summary>
public Label()
{
}
/// <summary>
/// Method which focuses <see cref="Target"/> input element
/// </summary>
private void LabelActivated(RoutedEventArgs args)
{
Target?.Focus();
args.Handled = Target != null;
}
/// <summary>
/// Handler of <see cref="IInputElement.PointerPressed"/> event
/// </summary>
/// <param name="e">Event Arguments</param>
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.PointerUpdateKind == PointerUpdateKind.LeftButtonPressed)
{
LabelActivated(e);
}
base.OnPointerPressed(e);
}
}
}

25
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -85,10 +85,19 @@ namespace Avalonia.Controls.Presenters
public static readonly StyledProperty<Thickness> PaddingProperty = public static readonly StyledProperty<Thickness> PaddingProperty =
Decorator.PaddingProperty.AddOwner<ContentPresenter>(); Decorator.PaddingProperty.AddOwner<ContentPresenter>();
/// <summary>
/// Defines the <see cref="RecognizesAccessKey"/> property
/// </summary>
public static readonly DirectProperty<ContentPresenter, bool> RecognizesAccessKeyProperty =
AvaloniaProperty.RegisterDirect<ContentPresenter, bool>(
nameof(RecognizesAccessKey),
cp => cp.RecognizesAccessKey, (cp, value) => cp.RecognizesAccessKey = value);
private IControl _child; private IControl _child;
private bool _createdChild; private bool _createdChild;
private IRecyclingDataTemplate _recyclingDataTemplate; private IRecyclingDataTemplate _recyclingDataTemplate;
private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper(); private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper();
private bool _recognizesAccessKey;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="ContentPresenter"/> class. /// Initializes static members of the <see cref="ContentPresenter"/> class.
@ -208,6 +217,15 @@ namespace Avalonia.Controls.Presenters
set { SetValue(PaddingProperty, value); } set { SetValue(PaddingProperty, value); }
} }
/// <summary>
/// Determine if <see cref="ContentPresenter"/> should use <see cref="AccessText"/> in its style
/// </summary>
public bool RecognizesAccessKey
{
get => _recognizesAccessKey;
set => SetAndRaise(RecognizesAccessKeyProperty, ref _recognizesAccessKey, value);
}
/// <summary> /// <summary>
/// Gets the host content control. /// Gets the host content control.
/// </summary> /// </summary>
@ -311,7 +329,12 @@ namespace Avalonia.Controls.Presenters
if (content != null && newChild == null) if (content != null && newChild == null)
{ {
var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? FuncDataTemplate.Default; var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ??
(
RecognizesAccessKey
? FuncDataTemplate.Access
: FuncDataTemplate.Default
);
if (dataTemplate is IRecyclingDataTemplate rdt) if (dataTemplate is IRecyclingDataTemplate rdt)
{ {

28
src/Avalonia.Controls/Templates/FuncDataTemplate.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls.Templates namespace Avalonia.Controls.Templates
{ {
@ -30,6 +31,31 @@ namespace Avalonia.Controls.Templates
}, },
true); true);
/// <summary>
/// The default data template used in the case where no matching data template is found
/// but <see cref="AccessText"/> should be used.
/// </summary>
public static readonly FuncDataTemplate Access =
new FuncDataTemplate<object>(
(data, s) =>
{
if (data != null)
{
var result = new AccessText();
result.Bind(TextBlock.TextProperty,
result.GetObservable(Control.DataContextProperty).Select(x => x?.ToString()));
return result;
}
else
{
return null;
}
},
true);
/// <summary>
/// The implementation of the <see cref="Match"/> method.
/// </summary>
private readonly Func<object, bool> _match; private readonly Func<object, bool> _match;
private readonly bool _supportsRecycling; private readonly bool _supportsRecycling;
@ -42,7 +68,7 @@ namespace Avalonia.Controls.Templates
/// </param> /// </param>
/// <param name="supportsRecycling">Whether the control can be recycled.</param> /// <param name="supportsRecycling">Whether the control can be recycled.</param>
public FuncDataTemplate( public FuncDataTemplate(
Type type, Type type,
Func<object, INameScope, IControl> build, Func<object, INameScope, IControl> build,
bool supportsRecycling = false) bool supportsRecycling = false)
: this(o => IsInstance(o, type), build, supportsRecycling) : this(o => IsInstance(o, type), build, supportsRecycling)

3
src/Avalonia.Themes.Default/DefaultTheme.xaml

@ -12,7 +12,8 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.CaptionButtons.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.CaptionButtons.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ComboBox.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ComboBox.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ComboBoxItem.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ComboBoxItem.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ContentControl.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ContentControl.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Label.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.GridSplitter.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.GridSplitter.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ItemsControl.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ItemsControl.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ListBox.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ListBox.xaml?assembly=Avalonia.Themes.Default"/>

17
src/Avalonia.Themes.Default/Label.xaml

@ -0,0 +1,17 @@
<Style xmlns="https://github.com/avaloniaui" Selector="Label">
<Setter Property="Padding" Value="3"/>
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
RecognizesAccessKey="True"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</Setter>
</Style>

3
src/Avalonia.Themes.Fluent/FluentTheme.xaml

@ -11,7 +11,8 @@
<StyleInclude Source="avares://Avalonia.Themes.Fluent/CheckBox.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/CheckBox.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ComboBox.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ComboBox.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ComboBoxItem.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ComboBoxItem.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ContentControl.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ContentControl.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Label.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/GridSplitter.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/GridSplitter.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ItemsControl.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ItemsControl.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/ListBox.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Fluent/ListBox.xaml"/>

17
src/Avalonia.Themes.Fluent/Label.xaml

@ -0,0 +1,17 @@
<Style xmlns="https://github.com/avaloniaui" Selector="Label">
<Setter Property="Padding" Value="3"/>
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
RecognizesAccessKey="True"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</Setter>
</Style>

176
src/Windows/Avalonia.Win32/FramebufferManager.cs

@ -1,41 +1,191 @@
using System; using System;
using System.Threading;
using Avalonia.Controls.Platform.Surfaces; using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
#nullable enable
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
class FramebufferManager : IFramebufferPlatformSurface, IDisposable internal class FramebufferManager : IFramebufferPlatformSurface, IDisposable
{ {
private const int _bytesPerPixel = 4;
private const PixelFormat _format = PixelFormat.Bgra8888;
private readonly IntPtr _hwnd; private readonly IntPtr _hwnd;
private WindowFramebuffer _fb; private readonly object _lock;
private readonly Action _onDisposeAction;
private FramebufferData? _framebufferData;
public FramebufferManager(IntPtr hwnd) public FramebufferManager(IntPtr hwnd)
{ {
_hwnd = hwnd; _hwnd = hwnd;
_lock = new object();
_onDisposeAction = DrawAndUnlock;
} }
public ILockedFramebuffer Lock() public ILockedFramebuffer Lock()
{ {
UnmanagedMethods.GetClientRect(_hwnd, out var rc); Monitor.Enter(_lock);
var width = Math.Max(1, rc.right - rc.left); LockedFramebuffer? fb = null;
var height = Math.Max(1, rc.bottom - rc.top);
if ((_fb == null || _fb.Size.Width != width || _fb.Size.Height != height)) try
{ {
_fb?.Deallocate(); UnmanagedMethods.GetClientRect(_hwnd, out var rc);
_fb = null;
_fb = new WindowFramebuffer(_hwnd, new PixelSize(width, height)); var width = Math.Max(1, rc.right - rc.left);
} var height = Math.Max(1, rc.bottom - rc.top);
if (_framebufferData is null || _framebufferData?.Size.Width != width || _framebufferData?.Size.Height != height)
{
_framebufferData?.Dispose();
_framebufferData = AllocateFramebufferData(width, height);
}
var framebufferData = _framebufferData.Value;
return _fb; return fb = new LockedFramebuffer(
framebufferData.Data.Address, framebufferData.Size, framebufferData.RowBytes,
GetCurrentDpi(), _format, _onDisposeAction);
}
finally
{
// We free the lock when for whatever reason framebuffer was not created.
// This allows for a potential retry later.
if (fb is null)
{
Monitor.Exit(_lock);
}
}
} }
public void Dispose() public void Dispose()
{ {
_fb?.Deallocate(); lock (_lock)
_fb = null; {
_framebufferData?.Dispose();
_framebufferData = null;
}
}
private void DrawAndUnlock()
{
try
{
if (_framebufferData.HasValue)
DrawToWindow(_hwnd, _framebufferData.Value);
}
finally
{
Monitor.Exit(_lock);
}
}
private Vector GetCurrentDpi()
{
if (UnmanagedMethods.ShCoreAvailable)
{
var monitor =
UnmanagedMethods.MonitorFromWindow(_hwnd, UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
if (UnmanagedMethods.GetDpiForMonitor(
monitor,
UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
out var dpix,
out var dpiy) == 0)
{
return new Vector(dpix, dpiy);
}
}
return new Vector(96, 96);
}
private static FramebufferData AllocateFramebufferData(int width, int height)
{
var bitmapBlob = AvaloniaLocator.Current.GetService<IRuntimePlatform>().AllocBlob(width * height * _bytesPerPixel);
return new FramebufferData(bitmapBlob, width, height);
}
private static void DrawToDevice(FramebufferData framebufferData, IntPtr hDC, int destX = 0, int destY = 0, int srcX = 0,
int srcY = 0, int width = -1,
int height = -1)
{
if (width == -1)
width = framebufferData.Size.Width;
if (height == -1)
height = framebufferData.Size.Height;
var bmpInfo = framebufferData.Header;
UnmanagedMethods.SetDIBitsToDevice(hDC, destX, destY, (uint)width, (uint)height, srcX, srcY,
0, (uint)framebufferData.Size.Height, framebufferData.Data.Address, ref bmpInfo, 0);
}
private static bool DrawToWindow(IntPtr hWnd, FramebufferData framebufferData, int destX = 0, int destY = 0, int srcX = 0,
int srcY = 0, int width = -1,
int height = -1)
{
if (framebufferData.Data.IsDisposed)
throw new ObjectDisposedException("Framebuffer");
if (hWnd == IntPtr.Zero)
return false;
var hDC = UnmanagedMethods.GetDC(hWnd);
if (hDC == IntPtr.Zero)
return false;
try
{
DrawToDevice(framebufferData, hDC, destX, destY, srcX, srcY, width, height);
}
finally
{
UnmanagedMethods.ReleaseDC(hWnd, hDC);
}
return true;
}
private readonly struct FramebufferData
{
public IUnmanagedBlob Data { get; }
public PixelSize Size { get; }
public int RowBytes => Size.Width * _bytesPerPixel;
public UnmanagedMethods.BITMAPINFOHEADER Header { get; }
public FramebufferData(IUnmanagedBlob data, int width, int height)
{
Data = data;
Size = new PixelSize(width, height);
var header = new UnmanagedMethods.BITMAPINFOHEADER();
header.Init();
header.biPlanes = 1;
header.biBitCount = _bytesPerPixel * 8;
header.Init();
header.biWidth = width;
header.biHeight = -height;
Header = header;
}
public void Dispose()
{
Data.Dispose();
}
} }
} }
} }

100
src/Windows/Avalonia.Win32/WindowFramebuffer.cs

@ -1,100 +0,0 @@
using System;
using Avalonia.Platform;
using Avalonia.Win32.Interop;
using PixelFormat = Avalonia.Platform.PixelFormat;
namespace Avalonia.Win32
{
public class WindowFramebuffer : ILockedFramebuffer
{
private readonly IntPtr _handle;
private IUnmanagedBlob _bitmapBlob;
private UnmanagedMethods.BITMAPINFOHEADER _bmpInfo;
public WindowFramebuffer(IntPtr handle, PixelSize size)
{
if (size.Width <= 0)
throw new ArgumentException("Width is less than zero");
if (size.Height <= 0)
throw new ArgumentException("Height is less than zero");
_handle = handle;
_bmpInfo.Init();
_bmpInfo.biPlanes = 1;
_bmpInfo.biBitCount = 32;
_bmpInfo.Init();
_bmpInfo.biWidth = size.Width;
_bmpInfo.biHeight = -size.Height;
_bitmapBlob = AvaloniaLocator.Current.GetService<IRuntimePlatform>().AllocBlob(size.Width * size.Height * 4);
}
~WindowFramebuffer()
{
Deallocate();
}
public IntPtr Address => _bitmapBlob.Address;
public int RowBytes => Size.Width * 4;
public PixelFormat Format => PixelFormat.Bgra8888;
public Vector Dpi
{
get
{
if (UnmanagedMethods.ShCoreAvailable)
{
uint dpix, dpiy;
var monitor = UnmanagedMethods.MonitorFromWindow(_handle,
UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
if (UnmanagedMethods.GetDpiForMonitor(
monitor,
UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
out dpix,
out dpiy) == 0)
{
return new Vector(dpix, dpiy);
}
}
return new Vector(96, 96);
}
}
public PixelSize Size => new PixelSize(_bmpInfo.biWidth, -_bmpInfo.biHeight);
public void DrawToDevice(IntPtr hDC, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1,
int height = -1)
{
if (width == -1)
width = Size.Width;
if (height == -1)
height = Size.Height;
UnmanagedMethods.SetDIBitsToDevice(hDC, destX, destY, (uint) width, (uint) height, srcX, srcY,
0, (uint)Size.Height, _bitmapBlob.Address, ref _bmpInfo, 0);
}
public bool DrawToWindow(IntPtr hWnd, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1,
int height = -1)
{
if (_bitmapBlob.IsDisposed)
throw new ObjectDisposedException("Framebuffer");
if (hWnd == IntPtr.Zero)
return false;
IntPtr hDC = UnmanagedMethods.GetDC(hWnd);
if (hDC == IntPtr.Zero)
return false;
DrawToDevice(hDC, destX, destY, srcX, srcY, width, height);
UnmanagedMethods.ReleaseDC(hWnd, hDC);
return true;
}
public void Dispose()
{
//It's not an *actual* dispose. This call means "We are done drawing"
DrawToWindow(_handle);
}
public void Deallocate() => _bitmapBlob.Dispose();
}
}
Loading…
Cancel
Save