Browse Source

Merge branch 'master' into more-skia-caching

pull/10303/head
DJGosnell 4 years ago
committed by GitHub
parent
commit
3f577d1654
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  2. 2
      native/Avalonia.Native/src/OSX/PopupImpl.mm
  3. 5
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  4. 29
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  5. 6
      native/Avalonia.Native/src/OSX/WindowImpl.h
  6. 101
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  7. 1
      samples/IntegrationTestApp/MainWindow.axaml
  8. 2
      samples/IntegrationTestApp/MainWindow.axaml.cs
  9. 4
      samples/MobileSandbox/MainView.xaml
  10. 42
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  11. 1
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  12. 127
      src/Android/Avalonia.Android/InputEditable.cs
  13. 2
      src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs
  14. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs
  15. 141
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  16. 4
      src/Android/Avalonia.Android/PlatformIconLoader.cs
  17. 6
      src/Android/Avalonia.Android/Stubs.cs
  18. 60
      src/Avalonia.Base/AvaloniaObject.cs
  19. 50
      src/Avalonia.Base/AvaloniaProperty.cs
  20. 23
      src/Avalonia.Base/Diagnostics/AvaloniaPropertyValue.cs
  21. 5
      src/Avalonia.Base/DirectPropertyBase.cs
  22. 23
      src/Avalonia.Base/Input/TextInput/ITextEditable.cs
  23. 11
      src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs
  24. 6
      src/Avalonia.Base/PropertyStore/EffectiveValue.cs
  25. 36
      src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
  26. 50
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  27. 4
      src/Avalonia.Base/StyledElement.cs
  28. 46
      src/Avalonia.Base/StyledProperty.cs
  29. 7
      src/Avalonia.Controls/ItemsControl.cs
  30. 33
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  31. 117
      src/Avalonia.Controls/TextBoxTextInputMethodClient.cs
  32. 2
      src/Avalonia.FreeDesktop/DBusHelper.cs
  33. 2
      src/Avalonia.FreeDesktop/DBusIme/X11DBusImeHelper.cs
  34. 2
      src/Avalonia.FreeDesktop/DBusMenuExporter.cs
  35. 6
      src/Avalonia.FreeDesktop/IX11InputMethod.cs
  36. 2
      src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoProvider.cs
  37. 2
      src/Avalonia.Headless/AvaloniaHeadlessPlatform.cs
  38. 2
      src/Avalonia.Native/CallbackBase.cs
  39. 2
      src/Avalonia.Native/MacOSMountedVolumeInfoProvider.cs
  40. 2
      src/Avalonia.Native/MenuActionCallback.cs
  41. 2
      src/Avalonia.Native/PredicateCallback.cs
  42. 2
      src/Avalonia.Native/ScreenImpl.cs
  43. 2
      src/Avalonia.X11/Glx/Glx.cs
  44. 2
      src/Avalonia.X11/Glx/GlxConsts.cs
  45. 6
      src/Avalonia.X11/Glx/GlxContext.cs
  46. 8
      src/Avalonia.X11/Glx/GlxDisplay.cs
  47. 8
      src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
  48. 2
      src/Avalonia.X11/Glx/GlxPlatformFeature.cs
  49. 2
      src/Avalonia.X11/Keysyms.cs
  50. 29
      src/Avalonia.X11/NativeDialogs/Gtk.cs
  51. 2
      src/Avalonia.X11/TransparencyHelper.cs
  52. 16
      src/Avalonia.X11/X11Clipboard.cs
  53. 4
      src/Avalonia.X11/X11CursorFactory.cs
  54. 8
      src/Avalonia.X11/X11Enums.cs
  55. 2
      src/Avalonia.X11/X11Exception.cs
  56. 2
      src/Avalonia.X11/X11Framebuffer.cs
  57. 2
      src/Avalonia.X11/X11FramebufferSurface.cs
  58. 12
      src/Avalonia.X11/X11Globals.cs
  59. 8
      src/Avalonia.X11/X11IconLoader.cs
  60. 2
      src/Avalonia.X11/X11Info.cs
  61. 2
      src/Avalonia.X11/X11KeyTransform.cs
  62. 10
      src/Avalonia.X11/X11NativeControlHost.cs
  63. 8
      src/Avalonia.X11/X11Platform.cs
  64. 32
      src/Avalonia.X11/X11PlatformThreading.cs
  65. 20
      src/Avalonia.X11/X11Screens.cs
  66. 9
      src/Avalonia.X11/X11Structs.cs
  67. 32
      src/Avalonia.X11/X11Window.Ime.cs
  68. 4
      src/Avalonia.X11/X11Window.Xim.cs
  69. 46
      src/Avalonia.X11/X11Window.cs
  70. 5
      src/Avalonia.X11/XError.cs
  71. 16
      src/Avalonia.X11/XI2Manager.cs
  72. 54
      src/Avalonia.X11/XIStructs.cs
  73. 10
      src/Avalonia.X11/XLib.cs
  74. 27
      src/Browser/Avalonia.Browser/AvaloniaView.cs
  75. 2
      src/Browser/Avalonia.Browser/Interop/InputHelper.cs
  76. 2
      src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs
  77. 20
      src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts
  78. 4
      src/Shared/RawEventGrouping.cs
  79. 5
      src/Skia/Avalonia.Skia/GlyphRunImpl.cs
  80. 4
      src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
  81. 2
      src/Skia/Avalonia.Skia/SKTypefaceCollection.cs
  82. 2
      src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs
  83. 22
      src/Skia/Avalonia.Skia/readme.md
  84. 6
      src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
  85. 2
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  86. 33
      src/Windows/Avalonia.Direct2D1/Disposable.cs
  87. 2
      src/Windows/Avalonia.Direct2D1/ILayerFactory.cs
  88. 4
      src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs
  89. 4
      src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileEnumerator.cs
  90. 2
      src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs
  91. 4
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  92. 4
      src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs
  93. 4
      src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs
  94. 4
      src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs
  95. 4
      src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
  96. 4
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs
  97. 4
      src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
  98. 4
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs
  99. 4
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
  100. 2
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs

17
native/Avalonia.Native/src/OSX/AvnWindow.mm

@ -223,6 +223,19 @@
} }
} }
// From chromium:
//
// > The delegate or the window class should implement this method so that
// > -[NSWindow isZoomed] can be then determined by whether or not the current
// > window frame is equal to the zoomed frame.
//
// If we don't implement this, then isZoomed always returns true for a non-
// resizable window ¯\_(ツ)_/¯
- (NSRect)windowWillUseStandardFrame:(NSWindow*)window
defaultFrame:(NSRect)newFrame {
return newFrame;
}
-(BOOL)canBecomeKeyWindow -(BOOL)canBecomeKeyWindow
{ {
if(_canBecomeKeyWindow) if(_canBecomeKeyWindow)
@ -261,10 +274,6 @@
-(void) setEnabled:(bool)enable -(void) setEnabled:(bool)enable
{ {
_isEnabled = enable; _isEnabled = enable;
[[self standardWindowButton:NSWindowCloseButton] setEnabled:enable];
[[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:enable];
[[self standardWindowButton:NSWindowZoomButton] setEnabled:enable];
} }
-(void)becomeKeyWindow -(void)becomeKeyWindow

2
native/Avalonia.Native/src/OSX/PopupImpl.mm

@ -29,7 +29,7 @@ private:
[Window setLevel:NSPopUpMenuWindowLevel]; [Window setLevel:NSPopUpMenuWindowLevel];
} }
protected: protected:
virtual NSWindowStyleMask GetStyle() override virtual NSWindowStyleMask CalculateStyleMask() override
{ {
return NSWindowStyleMaskBorderless; return NSWindowStyleMaskBorderless;
} }

5
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@ -105,9 +105,8 @@ BEGIN_INTERFACE_MAP()
virtual void BringToFront (); virtual void BringToFront ();
protected: protected:
virtual NSWindowStyleMask GetStyle(); virtual NSWindowStyleMask CalculateStyleMask() = 0;
virtual void UpdateStyle();
void UpdateStyle();
private: private:
void CreateNSWindow (bool isDialog); void CreateNSWindow (bool isDialog);

29
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@ -35,18 +35,14 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl,
lastSize = NSSize { 100, 100 }; lastSize = NSSize { 100, 100 };
lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX};
lastMinSize = NSSize { 0, 0 }; lastMinSize = NSSize { 0, 0 };
lastMenu = nullptr; lastMenu = nullptr;
CreateNSWindow(usePanel); CreateNSWindow(usePanel);
[Window setContentView:StandardContainer]; [Window setContentView:StandardContainer];
[Window setStyleMask:NSWindowStyleMaskBorderless];
[Window setBackingType:NSBackingStoreBuffered]; [Window setBackingType:NSBackingStoreBuffered];
[Window setContentMinSize:lastMinSize]; [Window setContentMinSize:lastMinSize];
[Window setContentMaxSize:lastMaxSize]; [Window setContentMaxSize:lastMaxSize];
[Window setOpaque:false]; [Window setOpaque:false];
} }
@ -564,12 +560,8 @@ bool WindowBaseImpl::IsModal() {
return false; return false;
} }
NSWindowStyleMask WindowBaseImpl::GetStyle() {
return NSWindowStyleMaskBorderless;
}
void WindowBaseImpl::UpdateStyle() { void WindowBaseImpl::UpdateStyle() {
[Window setStyleMask:GetStyle()]; [Window setStyleMask:CalculateStyleMask()];
} }
void WindowBaseImpl::CleanNSWindow() { void WindowBaseImpl::CleanNSWindow() {
@ -580,21 +572,12 @@ void WindowBaseImpl::CleanNSWindow() {
} }
} }
void WindowBaseImpl::CreateNSWindow(bool isDialog) { void WindowBaseImpl::CreateNSWindow(bool usePanel) {
if (isDialog) { if (usePanel) {
if (![Window isKindOfClass:[AvnPanel class]]) { Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:NSWindowStyleMaskBorderless];
CleanNSWindow(); [Window setHidesOnDeactivate:false];
Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()];
[Window setHidesOnDeactivate:false];
}
} else { } else {
if (![Window isKindOfClass:[AvnWindow class]]) { Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:NSWindowStyleMaskBorderless];
CleanNSWindow();
Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()];
}
} }
} }

6
native/Avalonia.Native/src/OSX/WindowImpl.h

@ -41,8 +41,6 @@ BEGIN_INTERFACE_MAP()
WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl); WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl);
void HideOrShowTrafficLights ();
virtual HRESULT Show (bool activate, bool isDialog) override; virtual HRESULT Show (bool activate, bool isDialog) override;
virtual HRESULT SetEnabled (bool enable) override; virtual HRESULT SetEnabled (bool enable) override;
@ -100,9 +98,11 @@ BEGIN_INTERFACE_MAP()
bool CanBecomeKeyWindow (); bool CanBecomeKeyWindow ();
protected: protected:
virtual NSWindowStyleMask GetStyle() override; virtual NSWindowStyleMask CalculateStyleMask() override;
void UpdateStyle () override;
private: private:
void ZOrderChildWindows();
void OnInitialiseNSWindow(); void OnInitialiseNSWindow();
NSString *_lastTitle; NSString *_lastTitle;
}; };

101
native/Avalonia.Native/src/OSX/WindowImpl.mm

@ -30,19 +30,6 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase
OnInitialiseNSWindow(); OnInitialiseNSWindow();
} }
void WindowImpl::HideOrShowTrafficLights() {
if (Window == nil) {
return;
}
bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull;
[[Window standardWindowButton:NSWindowCloseButton] setHidden:!hasTrafficLights];
[[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:!hasTrafficLights];
[[Window standardWindowButton:NSWindowZoomButton] setHidden:!hasTrafficLights];
}
void WindowImpl::OnInitialiseNSWindow(){ void WindowImpl::OnInitialiseNSWindow(){
[GetWindowProtocol() setCanBecomeKeyWindow:true]; [GetWindowProtocol() setCanBecomeKeyWindow:true];
@ -67,8 +54,6 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) {
WindowBaseImpl::Show(activate, isDialog); WindowBaseImpl::Show(activate, isDialog);
GetWindowState(&_actualWindowState); GetWindowState(&_actualWindowState);
HideOrShowTrafficLights();
return SetWindowState(_lastWindowState); return SetWindowState(_lastWindowState);
} }
} }
@ -134,14 +119,19 @@ void WindowImpl::BringToFront()
} }
[Window invalidateShadow]; [Window invalidateShadow];
ZOrderChildWindows();
}
}
void WindowImpl::ZOrderChildWindows()
{
for(auto iterator = _children.begin(); iterator != _children.end(); iterator++)
{
auto window = (*iterator)->Window;
for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) // #9565: Only bring window to front if it's on the currently active space
{ if ([window isOnActiveSpace]) {
auto window = (*iterator)->Window; (*iterator)->BringToFront();
// #9565: Only bring window to front if it's on the currently active space
if ([window isOnActiveSpace])
(*iterator)->BringToFront();
} }
} }
} }
@ -161,13 +151,15 @@ bool WindowImpl::CanBecomeKeyWindow()
void WindowImpl::StartStateTransition() { void WindowImpl::StartStateTransition() {
_transitioningWindowState = true; _transitioningWindowState = true;
UpdateStyle();
} }
void WindowImpl::EndStateTransition() { void WindowImpl::EndStateTransition() {
_transitioningWindowState = false; _transitioningWindowState = false;
UpdateStyle();
// Ensure correct order of child windows after fullscreen transition. // Ensure correct order of child windows after fullscreen transition.
BringToFront(); ZOrderChildWindows();
} }
SystemDecorations WindowImpl::Decorations() { SystemDecorations WindowImpl::Decorations() {
@ -225,16 +217,12 @@ bool WindowImpl::IsZoomed() {
} }
void WindowImpl::DoZoom() { void WindowImpl::DoZoom() {
switch (_decorations) { if (_decorations == SystemDecorationsNone ||
case SystemDecorationsNone: _decorations == SystemDecorationsBorderOnly ||
case SystemDecorationsBorderOnly: _canResize == false) {
[Window setFrame:[Window screen].visibleFrame display:true]; [Window setFrame:[Window screen].visibleFrame display:true];
break; } else {
[Window performZoom:Window];
case SystemDecorationsFull:
[Window performZoom:Window];
break;
} }
} }
@ -261,8 +249,6 @@ HRESULT WindowImpl::SetDecorations(SystemDecorations value) {
UpdateStyle(); UpdateStyle();
HideOrShowTrafficLights();
switch (_decorations) { switch (_decorations) {
case SystemDecorationsNone: case SystemDecorationsNone:
[Window setHasShadow:NO]; [Window setHasShadow:NO];
@ -419,9 +405,6 @@ HRESULT WindowImpl::SetExtendClientArea(bool enable) {
} }
[GetWindowProtocol() setIsExtended:enable]; [GetWindowProtocol() setIsExtended:enable];
HideOrShowTrafficLights();
UpdateStyle(); UpdateStyle();
} }
@ -577,14 +560,16 @@ bool WindowImpl::IsOwned() {
return _parent != nullptr; return _parent != nullptr;
} }
NSWindowStyleMask WindowImpl::GetStyle() { NSWindowStyleMask WindowImpl::CalculateStyleMask() {
unsigned long s = NSWindowStyleMaskBorderless; // Use the current style mask and only clear the flags we're going to be modifying.
unsigned long s = [Window styleMask] &
~(NSWindowStyleMaskFullSizeContentView |
NSWindowStyleMaskTitled |
NSWindowStyleMaskClosable |
NSWindowStyleMaskResizable |
NSWindowStyleMaskMiniaturizable |
NSWindowStyleMaskTexturedBackground);
if(_actualWindowState == FullScreen)
{
s |= NSWindowStyleMaskFullScreen;
}
switch (_decorations) { switch (_decorations) {
case SystemDecorationsNone: case SystemDecorationsNone:
s = s | NSWindowStyleMaskFullSizeContentView; s = s | NSWindowStyleMaskFullSizeContentView;
@ -597,7 +582,7 @@ NSWindowStyleMask WindowImpl::GetStyle() {
case SystemDecorationsFull: case SystemDecorationsFull:
s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable; s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;
if (_canResize && _isEnabled) { if ((_canResize && _isEnabled) || _transitioningWindowState) {
s = s | NSWindowStyleMaskResizable; s = s | NSWindowStyleMaskResizable;
} }
break; break;
@ -612,3 +597,25 @@ NSWindowStyleMask WindowImpl::GetStyle() {
} }
return s; return s;
} }
void WindowImpl::UpdateStyle() {
WindowBaseImpl::UpdateStyle();
if (Window == nil) {
return;
}
bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull;
NSButton* closeButton = [Window standardWindowButton:NSWindowCloseButton];
NSButton* miniaturizeButton = [Window standardWindowButton:NSWindowMiniaturizeButton];
NSButton* zoomButton = [Window standardWindowButton:NSWindowZoomButton];
[closeButton setHidden:!hasTrafficLights];
[closeButton setEnabled:_isEnabled];
[miniaturizeButton setHidden:!hasTrafficLights];
[miniaturizeButton setEnabled:_isEnabled];
[zoomButton setHidden:!hasTrafficLights];
[zoomButton setEnabled:_isEnabled && _canResize];
}

1
samples/IntegrationTestApp/MainWindow.axaml

@ -140,6 +140,7 @@
<ComboBoxItem Name="ShowWindowStateMaximized">Maximized</ComboBoxItem> <ComboBoxItem Name="ShowWindowStateMaximized">Maximized</ComboBoxItem>
<ComboBoxItem Name="ShowWindowStateFullScreen">FullScreen</ComboBoxItem> <ComboBoxItem Name="ShowWindowStateFullScreen">FullScreen</ComboBoxItem>
</ComboBox> </ComboBox>
<CheckBox Name="ShowWindowCanResize" IsChecked="True">Can Resize</CheckBox>
<Button Name="ShowWindow">Show Window</Button> <Button Name="ShowWindow">Show Window</Button>
<Button Name="SendToBack">Send to Back</Button> <Button Name="SendToBack">Send to Back</Button>
<Button Name="EnterFullscreen">Enter Fullscreen</Button> <Button Name="EnterFullscreen">Enter Fullscreen</Button>

2
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -66,11 +66,13 @@ namespace IntegrationTestApp
var locationComboBox = this.GetControl<ComboBox>("ShowWindowLocation"); var locationComboBox = this.GetControl<ComboBox>("ShowWindowLocation");
var stateComboBox = this.GetControl<ComboBox>("ShowWindowState"); var stateComboBox = this.GetControl<ComboBox>("ShowWindowState");
var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null; var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null;
var canResizeCheckBox = this.GetControl<CheckBox>("ShowWindowCanResize");
var owner = (Window)this.GetVisualRoot()!; var owner = (Window)this.GetVisualRoot()!;
var window = new ShowWindowTest var window = new ShowWindowTest
{ {
WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex, WindowStartupLocation = (WindowStartupLocation)locationComboBox.SelectedIndex,
CanResize = canResizeCheckBox.IsChecked.Value,
}; };
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)

4
samples/MobileSandbox/MainView.xaml

@ -5,8 +5,8 @@
x:DataType="mobileSandbox:MainView"> x:DataType="mobileSandbox:MainView">
<StackPanel Margin="100 50" Spacing="50"> <StackPanel Margin="100 50" Spacing="50">
<TextBlock Text="Login" Foreground="White" /> <TextBlock Text="Login" Foreground="White" />
<TextBox Watermark="Text" /> <TextBox TextInputOptions.Multiline="True" AcceptsReturn="True" Watermark="Text" Height="200" TextWrapping="Wrap"/>
<TextBox Watermark="Username" TextInputOptions.ContentType="Email" AcceptsReturn="True" TextInputOptions.ReturnKeyType="Search" /> <TextBox Watermark="Username" TextInputOptions.ContentType="Email" TextInputOptions.ReturnKeyType="Done" />
<TextBox Watermark="Password" PasswordChar="*" TextInputOptions.ContentType="Password" /> <TextBox Watermark="Password" PasswordChar="*" TextInputOptions.ContentType="Password" />
<TextBox Watermark="Pin" PasswordChar="*" TextInputOptions.ContentType="Digits" /> <TextBox Watermark="Pin" PasswordChar="*" TextInputOptions.ContentType="Digits" />
<Button Content="Login" Command="{Binding ButtonCommand}" /> <Button Content="Login" Command="{Binding ButtonCommand}" />

42
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -5,8 +5,10 @@ using Android.Text;
using Android.Views; using Android.Views;
using Android.Views.InputMethods; using Android.Views.InputMethods;
using Avalonia.Android.Platform.SkiaPlatform; using Avalonia.Android.Platform.SkiaPlatform;
using Avalonia.Controls.Presenters;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.TextInput; using Avalonia.Input.TextInput;
using Avalonia.Reactive;
namespace Avalonia.Android namespace Avalonia.Android
{ {
@ -32,7 +34,7 @@ namespace Avalonia.Android
ActionPrevious = 0x00000007, ActionPrevious = 0x00000007,
} }
class AndroidInputMethod<TView> : ITextInputMethodImpl, IAndroidInputMethod internal class AndroidInputMethod<TView> : ITextInputMethodImpl, IAndroidInputMethod
where TView : View, IInitEditorInfo where TView : View, IInitEditorInfo
{ {
private readonly TView _host; private readonly TView _host;
@ -68,23 +70,10 @@ namespace Avalonia.Android
public void SetClient(ITextInputMethodClient client) public void SetClient(ITextInputMethodClient client)
{ {
if (_client != null)
{
_client.SurroundingTextChanged -= SurroundingTextChanged;
}
if(_inputConnection != null)
{
_inputConnection.ComposingText = null;
_inputConnection.ComposingRegion = default;
}
_client = client; _client = client;
if (IsActive) if (IsActive)
{ {
_client.SurroundingTextChanged += SurroundingTextChanged;
_host.RequestFocus(); _host.RequestFocus();
_imm.RestartInput(View); _imm.RestartInput(View);
@ -101,24 +90,6 @@ namespace Avalonia.Android
} }
} }
private void SurroundingTextChanged(object sender, EventArgs e)
{
if (IsActive && _inputConnection != null)
{
var surroundingText = Client.SurroundingText;
_inputConnection.SurroundingText = surroundingText;
_imm.UpdateSelection(_host, surroundingText.AnchorOffset, surroundingText.CursorOffset, surroundingText.AnchorOffset, surroundingText.CursorOffset);
if (_inputConnection.ComposingText != null && !_inputConnection.IsCommiting && surroundingText.AnchorOffset == surroundingText.CursorOffset)
{
_inputConnection.CommitText(_inputConnection.ComposingText, 0);
_inputConnection.SetSelection(surroundingText.AnchorOffset, surroundingText.CursorOffset);
}
}
}
public void SetCursorRect(Rect rect) public void SetCursorRect(Rect rect)
{ {
@ -157,17 +128,20 @@ namespace Avalonia.Android
TextInputReturnKeyType.Search => (ImeFlags)CustomImeFlags.ActionSearch, TextInputReturnKeyType.Search => (ImeFlags)CustomImeFlags.ActionSearch,
TextInputReturnKeyType.Next => (ImeFlags)CustomImeFlags.ActionNext, TextInputReturnKeyType.Next => (ImeFlags)CustomImeFlags.ActionNext,
TextInputReturnKeyType.Previous => (ImeFlags)CustomImeFlags.ActionPrevious, TextInputReturnKeyType.Previous => (ImeFlags)CustomImeFlags.ActionPrevious,
_ => (ImeFlags)CustomImeFlags.ActionDone TextInputReturnKeyType.Done => (ImeFlags)CustomImeFlags.ActionDone,
_ => options.Multiline ? ImeFlags.NoEnterAction : (ImeFlags)CustomImeFlags.ActionDone
}; };
outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi; outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi;
_client.TextEditable = _inputConnection.InputEditable;
return _inputConnection; return _inputConnection;
}); });
} }
} }
public readonly record struct ComposingRegion internal readonly record struct ComposingRegion
{ {
private readonly int _start = -1; private readonly int _start = -1;
private readonly int _end = -1; private readonly int _end = -1;

1
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -5,6 +5,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver> <MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
<AndroidResgenNamespace>Avalonia.Android.Internal</AndroidResgenNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />

127
src/Android/Avalonia.Android/InputEditable.cs

@ -0,0 +1,127 @@
using System;
using Android.Runtime;
using Android.Text;
using Android.Views;
using Android.Views.InputMethods;
using Avalonia.Android.Platform.SkiaPlatform;
using Avalonia.Controls.Presenters;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
using Java.Lang;
using static System.Net.Mime.MediaTypeNames;
namespace Avalonia.Android
{
internal class InputEditable : SpannableStringBuilder, ITextEditable
{
private readonly TopLevelImpl _topLevel;
private readonly IAndroidInputMethod _inputMethod;
private readonly AvaloniaInputConnection _avaloniaInputConnection;
private int _currentBatchLevel;
private string _previousText;
private int _previousSelectionStart;
private int _previousSelectionEnd;
public event EventHandler TextChanged;
public event EventHandler SelectionChanged;
public event EventHandler CompositionChanged;
public InputEditable(TopLevelImpl topLevel, IAndroidInputMethod inputMethod, AvaloniaInputConnection avaloniaInputConnection)
{
_topLevel = topLevel;
_inputMethod = inputMethod;
_avaloniaInputConnection = avaloniaInputConnection;
}
public InputEditable(ICharSequence text) : base(text)
{
}
public InputEditable(string text) : base(text)
{
}
public InputEditable(ICharSequence text, int start, int end) : base(text, start, end)
{
}
public InputEditable(string text, int start, int end) : base(text, start, end)
{
}
protected InputEditable(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public int SelectionStart
{
get => Selection.GetSelectionStart(this); set
{
var end = SelectionEnd < 0 ? 0 : SelectionEnd;
_avaloniaInputConnection.SetSelection(value, end);
_inputMethod.IMM.UpdateSelection(_topLevel.View, value, end, value, end);
}
}
public int SelectionEnd
{
get => Selection.GetSelectionEnd(this); set
{
var start = SelectionStart < 0 ? 0 : SelectionStart;
_avaloniaInputConnection.SetSelection(start, value);
_inputMethod.IMM.UpdateSelection(_topLevel.View, start, value, start, value);
}
}
public string? Text
{
get => ToString(); set
{
if (Text != value)
{
Clear();
Insert(0, value ?? "");
}
}
}
public int CompositionStart => BaseInputConnection.GetComposingSpanStart(this);
public int CompositionEnd => BaseInputConnection.GetComposingSpanEnd(this);
public void BeginBatchEdit()
{
_currentBatchLevel++;
if (_currentBatchLevel == 1)
{
_previousText = ToString();
_previousSelectionStart = SelectionStart;
_previousSelectionEnd = SelectionEnd;
}
}
public void EndBatchEdit()
{
if (_currentBatchLevel == 1)
{
if(_previousText != Text)
{
TextChanged?.Invoke(this, EventArgs.Empty);
}
if (_previousSelectionStart != SelectionStart || _previousSelectionEnd != SelectionEnd)
{
SelectionChanged?.Invoke(this, EventArgs.Empty);
}
}
_currentBatchLevel--;
}
public void RaiseCompositionChanged()
{
CompositionChanged?.Invoke(this, EventArgs.Empty);
}
}
}

2
src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs

@ -5,7 +5,7 @@ using Avalonia.Input;
namespace Avalonia.Android.Platform.Input namespace Avalonia.Android.Platform.Input
{ {
public class AndroidKeyboardDevice : KeyboardDevice, IKeyboardDevice { internal class AndroidKeyboardDevice : KeyboardDevice, IKeyboardDevice {
private static readonly Dictionary<Keycode, Key> KeyDic = new Dictionary<Keycode, Key> private static readonly Dictionary<Keycode, Key> KeyDic = new Dictionary<Keycode, Key>
{ {
// { Keycode.Cancel?, Key.Cancel }, // { Keycode.Cancel?, Key.Cancel },

2
src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs

@ -10,7 +10,7 @@ using Avalonia.Platform;
namespace Avalonia.Android namespace Avalonia.Android
{ {
public abstract class InvalidationAwareSurfaceView : SurfaceView, ISurfaceHolderCallback, IPlatformNativeSurfaceHandle internal abstract class InvalidationAwareSurfaceView : SurfaceView, ISurfaceHolderCallback, IPlatformNativeSurfaceHandle
{ {
bool _invalidateQueued; bool _invalidateQueued;
readonly object _lock = new object(); readonly object _lock = new object();

141
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -28,6 +28,7 @@ using Math = System.Math;
using AndroidRect = Android.Graphics.Rect; using AndroidRect = Android.Graphics.Rect;
using Window = Android.Views.Window; using Window = Android.Views.Window;
using Android.Graphics.Drawables; using Android.Graphics.Drawables;
using Java.Util;
namespace Avalonia.Android.Platform.SkiaPlatform namespace Avalonia.Android.Platform.SkiaPlatform
{ {
@ -410,159 +411,73 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{ {
private readonly TopLevelImpl _topLevel; private readonly TopLevelImpl _topLevel;
private readonly IAndroidInputMethod _inputMethod; private readonly IAndroidInputMethod _inputMethod;
private readonly InputEditable _editable;
public AvaloniaInputConnection(TopLevelImpl topLevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true) public AvaloniaInputConnection(TopLevelImpl topLevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true)
{ {
_topLevel = topLevel; _topLevel = topLevel;
_inputMethod = inputMethod; _inputMethod = inputMethod;
_editable = new InputEditable(_topLevel, _inputMethod, this);
} }
public TextInputMethodSurroundingText SurroundingText { get; set; } public override IEditable Editable => _editable;
public string ComposingText { get; internal set; } internal InputEditable InputEditable => _editable;
public ComposingRegion? ComposingRegion { get; internal set; }
public bool IsComposing => !string.IsNullOrEmpty(ComposingText);
public bool IsCommiting { get; private set; }
public override bool SetComposingRegion(int start, int end) public override bool SetComposingRegion(int start, int end)
{ {
//System.Diagnostics.Debug.WriteLine($"Composing Region: [{start}|{end}] {SurroundingText.Text?.Substring(start, end - start)}"); var ret = base.SetComposingRegion(start, end);
ComposingRegion = new ComposingRegion(start, end); InputEditable.RaiseCompositionChanged();
return base.SetComposingRegion(start, end); return ret;
} }
public override bool SetComposingText(ICharSequence text, int newCursorPosition) public override bool SetComposingText(ICharSequence text, int newCursorPosition)
{ {
var composingText = text.ToString(); var composingText = text.ToString();
ComposingText = composingText; if (string.IsNullOrEmpty(composingText))
_inputMethod.Client?.SetPreeditText(ComposingText);
return base.SetComposingText(text, newCursorPosition);
}
public override bool FinishComposingText()
{
if (!string.IsNullOrEmpty(ComposingText))
{ {
CommitText(ComposingText, ComposingText.Length); return CommitText(text, newCursorPosition);
} }
else else
{ {
ComposingRegion = new ComposingRegion(SurroundingText.CursorOffset, SurroundingText.CursorOffset); var ret = base.SetComposingText(text, newCursorPosition);
}
return base.FinishComposingText();
}
public override ICharSequence GetTextBeforeCursorFormatted(int length, [GeneratedEnum] GetTextFlags flags)
{
if (!string.IsNullOrEmpty(SurroundingText.Text) && length > 0)
{
var start = System.Math.Max(SurroundingText.CursorOffset - length, 0);
var end = System.Math.Min(start + length - 1, SurroundingText.CursorOffset); InputEditable.RaiseCompositionChanged();
var text = SurroundingText.Text.Substring(start, end - start); return ret;
//System.Diagnostics.Debug.WriteLine($"Text Before: {text}");
return new Java.Lang.String(text);
} }
return null;
} }
public override ICharSequence GetTextAfterCursorFormatted(int length, [GeneratedEnum] GetTextFlags flags) public override bool BeginBatchEdit()
{ {
if (!string.IsNullOrEmpty(SurroundingText.Text)) _editable.BeginBatchEdit();
{
var start = SurroundingText.CursorOffset;
var end = System.Math.Min(start + length, SurroundingText.Text.Length);
var text = SurroundingText.Text.Substring(start, end - start);
//System.Diagnostics.Debug.WriteLine($"Text After: {text}"); return base.BeginBatchEdit();
return new Java.Lang.String(text);
}
return null;
} }
public override bool CommitText(ICharSequence text, int newCursorPosition) public override bool EndBatchEdit()
{ {
IsCommiting = true; var ret = base.EndBatchEdit();
var committedText = text.ToString(); _editable.EndBatchEdit();
_inputMethod.Client.SetPreeditText(null);
int? start, end; return ret;
if(SurroundingText.CursorOffset != SurroundingText.AnchorOffset)
{
start = Math.Min(SurroundingText.CursorOffset, SurroundingText.AnchorOffset);
end = Math.Max(SurroundingText.CursorOffset, SurroundingText.AnchorOffset);
}
else if (ComposingRegion != null)
{
start = ComposingRegion?.Start;
end = ComposingRegion?.End;
ComposingRegion = null;
}
else
{
start = end = _inputMethod.Client.SurroundingText.CursorOffset;
}
_inputMethod.Client.SelectInSurroundingText((int)start, (int)end);
var time = DateTime.Now.TimeOfDay;
var rawTextEvent = new RawTextInputEventArgs(KeyboardDevice.Instance, (ulong)time.Ticks, _topLevel.InputRoot, committedText);
_topLevel.Input(rawTextEvent);
ComposingText = null;
ComposingRegion = new ComposingRegion(newCursorPosition, newCursorPosition);
return base.CommitText(text, newCursorPosition);
} }
public override bool DeleteSurroundingText(int beforeLength, int afterLength) public override bool FinishComposingText()
{ {
var surroundingText = _inputMethod.Client.SurroundingText; var ret = base.FinishComposingText();
InputEditable.RaiseCompositionChanged();
var selectionStart = surroundingText.CursorOffset; return ret;
_inputMethod.Client.SelectInSurroundingText(selectionStart - beforeLength, selectionStart + afterLength);
_inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel));
surroundingText = _inputMethod.Client.SurroundingText;
selectionStart = surroundingText.CursorOffset;
ComposingRegion = new ComposingRegion(selectionStart, selectionStart);
return base.DeleteSurroundingText(beforeLength, afterLength);
} }
public override bool SetSelection(int start, int end) public override bool CommitText(ICharSequence text, int newCursorPosition)
{ {
_inputMethod.Client.SelectInSurroundingText(start, end); var ret = base.CommitText(text, newCursorPosition);
InputEditable.RaiseCompositionChanged();
ComposingRegion = new ComposingRegion(start, end); return ret;
return base.SetSelection(start, end);
} }
public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode) public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode)

4
src/Android/Avalonia.Android/PlatformIconLoader.cs

@ -3,7 +3,7 @@ using Avalonia.Platform;
namespace Avalonia.Android namespace Avalonia.Android
{ {
class PlatformIconLoader : IPlatformIconLoader internal class PlatformIconLoader : IPlatformIconLoader
{ {
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
{ {
@ -29,7 +29,7 @@ namespace Avalonia.Android
} }
// Stores the icon created as a stream to support saving even though an icon is never shown // Stores the icon created as a stream to support saving even though an icon is never shown
public class FakeIcon : IWindowIconImpl internal class FakeIcon : IWindowIconImpl
{ {
private Stream stream = new MemoryStream(); private Stream stream = new MemoryStream();

6
src/Android/Avalonia.Android/Stubs.cs

@ -4,7 +4,7 @@ using Avalonia.Platform;
namespace Avalonia.Android namespace Avalonia.Android
{ {
class WindowingPlatformStub : IWindowingPlatform internal class WindowingPlatformStub : IWindowingPlatform
{ {
public IWindowImpl CreateWindow() => throw new NotSupportedException(); public IWindowImpl CreateWindow() => throw new NotSupportedException();
@ -13,7 +13,7 @@ namespace Avalonia.Android
public ITrayIconImpl CreateTrayIcon() => null; public ITrayIconImpl CreateTrayIcon() => null;
} }
class PlatformIconLoaderStub : IPlatformIconLoader internal class PlatformIconLoaderStub : IPlatformIconLoader
{ {
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
{ {
@ -38,7 +38,7 @@ namespace Avalonia.Android
} }
} }
public class IconStub : IWindowIconImpl internal class IconStub : IWindowIconImpl
{ {
private readonly MemoryStream _ms; private readonly MemoryStream _ms;

60
src/Avalonia.Base/AvaloniaObject.cs

@ -118,7 +118,7 @@ namespace Avalonia
{ {
_ = property ?? throw new ArgumentNullException(nameof(property)); _ = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess(); VerifyAccess();
_values.ClearLocalValue(property); _values.ClearValue(property);
} }
/// <summary> /// <summary>
@ -152,7 +152,7 @@ namespace Avalonia
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess(); VerifyAccess();
_values.ClearLocalValue(property); _values.ClearValue(property);
} }
/// <summary> /// <summary>
@ -329,7 +329,7 @@ namespace Avalonia
if (value is UnsetValueType) if (value is UnsetValueType)
{ {
if (priority == BindingPriority.LocalValue) if (priority == BindingPriority.LocalValue)
_values.ClearLocalValue(property); _values.ClearValue(property);
} }
else if (value is not DoNothingType) else if (value is not DoNothingType)
{ {
@ -355,6 +355,57 @@ namespace Avalonia
SetDirectValueUnchecked(property, value); SetDirectValueUnchecked(property, value);
} }
/// <summary>
/// Sets the value of a dependency property without changing its value source.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <remarks>
/// This method is used by a component that programmatically sets the value of one of its
/// own properties without disabling an application's declared use of the property. The
/// method changes the effective value of the property, but existing data bindings and
/// styles will continue to work.
///
/// The new value will have the property's current <see cref="BindingPriority"/>, even if
/// that priority is <see cref="BindingPriority.Unset"/> or
/// <see cref="BindingPriority.Inherited"/>.
/// </remarks>
public void SetCurrentValue(AvaloniaProperty property, object? value) =>
property.RouteSetCurrentValue(this, value);
/// <summary>
/// Sets the value of a dependency property without changing its value source.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <remarks>
/// This method is used by a component that programmatically sets the value of one of its
/// own properties without disabling an application's declared use of the property. The
/// method changes the effective value of the property, but existing data bindings and
/// styles will continue to work.
///
/// The new value will have the property's current <see cref="BindingPriority"/>, even if
/// that priority is <see cref="BindingPriority.Unset"/> or
/// <see cref="BindingPriority.Inherited"/>.
/// </remarks>
public void SetCurrentValue<T>(StyledProperty<T> property, T value)
{
_ = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess();
LogPropertySet(property, value, BindingPriority.LocalValue);
if (value is UnsetValueType)
{
_values.ClearValue(property);
}
else if (value is not DoNothingType)
{
_values.SetCurrentValue(property, value);
}
}
/// <summary> /// <summary>
/// Binds a <see cref="AvaloniaProperty"/> to an observable. /// Binds a <see cref="AvaloniaProperty"/> to an observable.
/// </summary> /// </summary>
@ -547,7 +598,8 @@ namespace Avalonia
property, property,
GetValue(property), GetValue(property),
BindingPriority.LocalValue, BindingPriority.LocalValue,
null); null,
false);
} }
return _values.GetDiagnostic(property); return _values.GetDiagnostic(property);

50
src/Avalonia.Base/AvaloniaProperty.cs

@ -225,13 +225,8 @@ namespace Avalonia
/// <param name="defaultValue">The default value of the property.</param> /// <param name="defaultValue">The default value of the property.</param>
/// <param name="inherits">Whether the property inherits its value.</param> /// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param> /// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="validate">A value validation callback.</param> /// <param name="validate">A value validation callback.</param>
/// <param name="coerce">A value coercion callback.</param> /// <param name="coerce">A value coercion callback.</param>
/// <param name="notifying">
/// A method that gets called before and after the property starts being notified on an
/// object; the bool argument will be true before and false afterwards. This callback is
/// intended to support IsDataContextChanging.
/// </param>
/// <returns>A <see cref="StyledProperty{TValue}"/></returns> /// <returns>A <see cref="StyledProperty{TValue}"/></returns>
public static StyledProperty<TValue> Register<TOwner, TValue>( public static StyledProperty<TValue> Register<TOwner, TValue>(
string name, string name,
@ -239,8 +234,40 @@ namespace Avalonia
bool inherits = false, bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay, BindingMode defaultBindingMode = BindingMode.OneWay,
Func<TValue, bool>? validate = null, Func<TValue, bool>? validate = null,
Func<AvaloniaObject, TValue, TValue>? coerce = null, Func<AvaloniaObject, TValue, TValue>? coerce = null)
Action<AvaloniaObject, bool>? notifying = null) where TOwner : AvaloniaObject
{
_ = name ?? throw new ArgumentNullException(nameof(name));
var metadata = new StyledPropertyMetadata<TValue>(
defaultValue,
defaultBindingMode: defaultBindingMode,
coerce: coerce);
var result = new StyledProperty<TValue>(
name,
typeof(TOwner),
metadata,
inherits,
validate);
AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), result);
return result;
}
/// <inheritdoc cref="Register{TOwner, TValue}" />
/// <param name="notifying">
/// A method that gets called before and after the property starts being notified on an
/// object; the bool argument will be true before and false afterwards. This callback is
/// intended to support IsDataContextChanging.
/// </param>
internal static StyledProperty<TValue> Register<TOwner, TValue>(
string name,
TValue defaultValue,
bool inherits,
BindingMode defaultBindingMode,
Func<TValue, bool>? validate,
Func<AvaloniaObject, TValue, TValue>? coerce,
Action<AvaloniaObject, bool>? notifying)
where TOwner : AvaloniaObject where TOwner : AvaloniaObject
{ {
_ = name ?? throw new ArgumentNullException(nameof(name)); _ = name ?? throw new ArgumentNullException(nameof(name));
@ -496,6 +523,13 @@ namespace Avalonia
object? value, object? value,
BindingPriority priority); BindingPriority priority);
/// <summary>
/// Routes an untyped SetCurrentValue call to a typed call.
/// </summary>
/// <param name="o">The object instance.</param>
/// <param name="value">The value.</param>
internal abstract void RouteSetCurrentValue(AvaloniaObject o, object? value);
/// <summary> /// <summary>
/// Routes an untyped Bind call to a typed call. /// Routes an untyped Bind call to a typed call.
/// </summary> /// </summary>

23
src/Avalonia.Base/Diagnostics/AvaloniaPropertyValue.cs

@ -3,28 +3,23 @@ using Avalonia.Data;
namespace Avalonia.Diagnostics namespace Avalonia.Diagnostics
{ {
/// <summary> /// <summary>
/// Holds diagnostic-related information about the value of a <see cref="AvaloniaProperty"/> /// Holds diagnostic-related information about the value of an <see cref="AvaloniaProperty"/>
/// on a <see cref="AvaloniaObject"/>. /// on an <see cref="AvaloniaObject"/>.
/// </summary> /// </summary>
public class AvaloniaPropertyValue public class AvaloniaPropertyValue
{ {
/// <summary> internal AvaloniaPropertyValue(
/// Initializes a new instance of the <see cref="AvaloniaPropertyValue"/> class.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="value">The current property value.</param>
/// <param name="priority">The priority of the current value.</param>
/// <param name="diagnostic">A diagnostic string.</param>
public AvaloniaPropertyValue(
AvaloniaProperty property, AvaloniaProperty property,
object? value, object? value,
BindingPriority priority, BindingPriority priority,
string? diagnostic) string? diagnostic,
bool isOverriddenCurrentValue)
{ {
Property = property; Property = property;
Value = value; Value = value;
Priority = priority; Priority = priority;
Diagnostic = diagnostic; Diagnostic = diagnostic;
IsOverriddenCurrentValue = isOverriddenCurrentValue;
} }
/// <summary> /// <summary>
@ -46,5 +41,11 @@ namespace Avalonia.Diagnostics
/// Gets a diagnostic string. /// Gets a diagnostic string.
/// </summary> /// </summary>
public string? Diagnostic { get; } public string? Diagnostic { get; }
/// <summary>
/// Gets a value indicating whether the <see cref="Value"/> was overridden by a call to
/// <see cref="AvaloniaObject.SetCurrentValue{T}"/>.
/// </summary>
public bool IsOverriddenCurrentValue { get; }
} }
} }

5
src/Avalonia.Base/DirectPropertyBase.cs

@ -152,6 +152,11 @@ namespace Avalonia
return null; return null;
} }
internal override void RouteSetCurrentValue(AvaloniaObject o, object? value)
{
RouteSetValue(o, value, BindingPriority.LocalValue);
}
/// <summary> /// <summary>
/// Routes an untyped Bind call to a typed call. /// Routes an untyped Bind call to a typed call.
/// </summary> /// </summary>

23
src/Avalonia.Base/Input/TextInput/ITextEditable.cs

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Input.TextInput
{
[NotClientImplementable]
public interface ITextEditable
{
event EventHandler TextChanged;
event EventHandler SelectionChanged;
event EventHandler CompositionChanged;
int SelectionStart { get; set; }
int SelectionEnd { get; set; }
int CompositionStart { get; }
int CompositionEnd { get; }
string? Text { get; set; }
}
}

11
src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.Media.TextFormatting;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Input.TextInput namespace Avalonia.Input.TextInput
@ -30,6 +31,11 @@ namespace Avalonia.Input.TextInput
/// </summary> /// </summary>
void SetPreeditText(string? text); void SetPreeditText(string? text);
/// <summary>
/// Sets the current composing region. This doesn't remove the composing text from the commited text.
/// </summary>
void SetComposingRegion(TextRange? region);
/// <summary> /// <summary>
/// Indicates if text input client is capable of providing the text around the cursor /// Indicates if text input client is capable of providing the text around the cursor
/// </summary> /// </summary>
@ -43,6 +49,11 @@ namespace Avalonia.Input.TextInput
/// </summary> /// </summary>
event EventHandler? SurroundingTextChanged; event EventHandler? SurroundingTextChanged;
/// <summary>
/// Gets or sets a platform editable. Text and selection changes made in the editable are forwarded to the IM client.
/// </summary>
ITextEditable? TextEditable { get; set; }
void SelectInSurroundingText(int start, int end); void SelectInSurroundingText(int start, int end);
} }

6
src/Avalonia.Base/PropertyStore/EffectiveValue.cs

@ -29,6 +29,12 @@ namespace Avalonia.PropertyStore
/// </summary> /// </summary>
public BindingPriority BasePriority { get; protected set; } public BindingPriority BasePriority { get; protected set; }
/// <summary>
/// Gets a value indicating whether the <see cref="Value"/> was overridden by a call to
/// <see cref="AvaloniaObject.SetCurrentValue{T}"/>.
/// </summary>
public bool IsOverridenCurrentValue { get; set; }
/// <summary> /// <summary>
/// Begins a reevaluation pass on the effective value. /// Begins a reevaluation pass on the effective value.
/// </summary> /// </summary>

36
src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs

@ -19,13 +19,16 @@ namespace Avalonia.PropertyStore
private T? _baseValue; private T? _baseValue;
private UncommonFields? _uncommon; private UncommonFields? _uncommon;
public EffectiveValue(AvaloniaObject owner, StyledProperty<T> property) public EffectiveValue(
AvaloniaObject owner,
StyledProperty<T> property,
EffectiveValue<T>? inherited)
{ {
Priority = BindingPriority.Unset; Priority = BindingPriority.Unset;
BasePriority = BindingPriority.Unset; BasePriority = BindingPriority.Unset;
_metadata = property.GetMetadata(owner.GetType()); _metadata = property.GetMetadata(owner.GetType());
var value = _metadata.DefaultValue; var value = inherited is null ? _metadata.DefaultValue : inherited.Value;
if (property.HasCoercion && _metadata.CoerceValue is { } coerce) if (property.HasCoercion && _metadata.CoerceValue is { } coerce)
{ {
@ -57,7 +60,7 @@ namespace Avalonia.PropertyStore
Debug.Assert(priority != BindingPriority.LocalValue); Debug.Assert(priority != BindingPriority.LocalValue);
UpdateValueEntry(value, priority); UpdateValueEntry(value, priority);
SetAndRaiseCore(owner, (StyledProperty<T>)value.Property, GetValue(value), priority); SetAndRaiseCore(owner, (StyledProperty<T>)value.Property, GetValue(value), priority, false);
} }
public void SetLocalValueAndRaise( public void SetLocalValueAndRaise(
@ -65,7 +68,16 @@ namespace Avalonia.PropertyStore
StyledProperty<T> property, StyledProperty<T> property,
T value) T value)
{ {
SetAndRaiseCore(owner, property, value, BindingPriority.LocalValue); SetAndRaiseCore(owner, property, value, BindingPriority.LocalValue, false);
}
public void SetCurrentValueAndRaise(
ValueStore owner,
StyledProperty<T> property,
T value)
{
IsOverridenCurrentValue = true;
SetAndRaiseCore(owner, property, value, Priority, true);
} }
public bool TryGetBaseValue([MaybeNullWhen(false)] out T value) public bool TryGetBaseValue([MaybeNullWhen(false)] out T value)
@ -98,7 +110,7 @@ namespace Avalonia.PropertyStore
Debug.Assert(Priority != BindingPriority.Animation); Debug.Assert(Priority != BindingPriority.Animation);
Debug.Assert(BasePriority != BindingPriority.Unset); Debug.Assert(BasePriority != BindingPriority.Unset);
UpdateValueEntry(null, BindingPriority.Animation); UpdateValueEntry(null, BindingPriority.Animation);
SetAndRaiseCore(owner, (StyledProperty<T>)property, _baseValue!, BasePriority); SetAndRaiseCore(owner, (StyledProperty<T>)property, _baseValue!, BasePriority, false);
} }
public override void CoerceValue(ValueStore owner, AvaloniaProperty property) public override void CoerceValue(ValueStore owner, AvaloniaProperty property)
@ -158,15 +170,16 @@ namespace Avalonia.PropertyStore
ValueStore owner, ValueStore owner,
StyledProperty<T> property, StyledProperty<T> property,
T value, T value,
BindingPriority priority) BindingPriority priority,
bool isOverriddenCurrentValue)
{ {
Debug.Assert(priority < BindingPriority.Inherited);
var oldValue = Value; var oldValue = Value;
var valueChanged = false; var valueChanged = false;
var baseValueChanged = false; var baseValueChanged = false;
var v = value; var v = value;
IsOverridenCurrentValue = isOverriddenCurrentValue;
if (_uncommon?._coerce is { } coerce) if (_uncommon?._coerce is { } coerce)
v = coerce(owner.Owner, value); v = coerce(owner.Owner, value);
@ -209,7 +222,6 @@ namespace Avalonia.PropertyStore
T baseValue, T baseValue,
BindingPriority basePriority) BindingPriority basePriority)
{ {
Debug.Assert(priority < BindingPriority.Inherited);
Debug.Assert(basePriority > BindingPriority.Animation); Debug.Assert(basePriority > BindingPriority.Animation);
Debug.Assert(priority <= basePriority); Debug.Assert(priority <= basePriority);
@ -225,7 +237,7 @@ namespace Avalonia.PropertyStore
bv = coerce(owner.Owner, baseValue); bv = coerce(owner.Owner, baseValue);
} }
if (priority != BindingPriority.Unset && !EqualityComparer<T>.Default.Equals(Value, v)) if (!EqualityComparer<T>.Default.Equals(Value, v))
{ {
Value = v; Value = v;
valueChanged = true; valueChanged = true;
@ -233,9 +245,7 @@ namespace Avalonia.PropertyStore
_uncommon._uncoercedValue = value; _uncommon._uncoercedValue = value;
} }
if (priority != BindingPriority.Unset && if (!EqualityComparer<T>.Default.Equals(_baseValue, bv))
(BasePriority == BindingPriority.Unset ||
!EqualityComparer<T>.Default.Equals(_baseValue, bv)))
{ {
_baseValue = v; _baseValue = v;
baseValueChanged = true; baseValueChanged = true;

50
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -7,7 +7,6 @@ using Avalonia.Data;
using Avalonia.Diagnostics; using Avalonia.Diagnostics;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Utilities; using Avalonia.Utilities;
using static Avalonia.Rendering.Composition.Animations.PropertySetSnapshot;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
{ {
@ -156,11 +155,12 @@ namespace Avalonia.PropertyStore
return observer; return observer;
} }
public void ClearLocalValue(AvaloniaProperty property) public void ClearValue(AvaloniaProperty property)
{ {
if (TryGetEffectiveValue(property, out var effective) && if (TryGetEffectiveValue(property, out var effective) &&
effective.Priority == BindingPriority.LocalValue) (effective.Priority == BindingPriority.LocalValue || effective.IsOverridenCurrentValue))
{ {
effective.IsOverridenCurrentValue = false;
ReevaluateEffectiveValue(property, effective, ignoreLocalValue: true); ReevaluateEffectiveValue(property, effective, ignoreLocalValue: true);
} }
} }
@ -184,7 +184,7 @@ namespace Avalonia.PropertyStore
} }
else else
{ {
var effectiveValue = new EffectiveValue<T>(Owner, property); var effectiveValue = CreateEffectiveValue(property);
AddEffectiveValue(property, effectiveValue); AddEffectiveValue(property, effectiveValue);
effectiveValue.SetAndRaise(this, result, priority); effectiveValue.SetAndRaise(this, result, priority);
} }
@ -200,7 +200,7 @@ namespace Avalonia.PropertyStore
} }
else else
{ {
var effectiveValue = new EffectiveValue<T>(Owner, property); var effectiveValue = CreateEffectiveValue(property);
AddEffectiveValue(property, effectiveValue); AddEffectiveValue(property, effectiveValue);
effectiveValue.SetLocalValueAndRaise(this, property, value); effectiveValue.SetLocalValueAndRaise(this, property, value);
} }
@ -209,6 +209,20 @@ namespace Avalonia.PropertyStore
} }
} }
public void SetCurrentValue<T>(StyledProperty<T> property, T value)
{
if (TryGetEffectiveValue(property, out var v))
{
((EffectiveValue<T>)v).SetCurrentValueAndRaise(this, property, value);
}
else
{
var effectiveValue = CreateEffectiveValue(property);
AddEffectiveValue(property, effectiveValue);
effectiveValue.SetCurrentValueAndRaise(this, property, value);
}
}
public object? GetValue(AvaloniaProperty property) public object? GetValue(AvaloniaProperty property)
{ {
if (_effectiveValues.TryGetValue(property, out var v)) if (_effectiveValues.TryGetValue(property, out var v))
@ -235,12 +249,7 @@ namespace Avalonia.PropertyStore
return false; return false;
} }
public bool IsSet(AvaloniaProperty property) public bool IsSet(AvaloniaProperty property) => _effectiveValues.TryGetValue(property, out _);
{
if (_effectiveValues.TryGetValue(property, out var v))
return v.Priority < BindingPriority.Inherited;
return false;
}
public void CoerceValue(AvaloniaProperty property) public void CoerceValue(AvaloniaProperty property)
{ {
@ -278,6 +287,16 @@ namespace Avalonia.PropertyStore
return false; return false;
} }
public EffectiveValue<T> CreateEffectiveValue<T>(StyledProperty<T> property)
{
EffectiveValue<T>? inherited = null;
if (property.Inherits && TryGetInheritedValue(property, out var v))
inherited = (EffectiveValue<T>)v;
return new EffectiveValue<T>(Owner, property, inherited);
}
public void SetInheritanceParent(AvaloniaObject? newParent) public void SetInheritanceParent(AvaloniaObject? newParent)
{ {
var values = AvaloniaPropertyDictionaryPool<OldNewValue>.Get(); var values = AvaloniaPropertyDictionaryPool<OldNewValue>.Get();
@ -490,7 +509,7 @@ namespace Avalonia.PropertyStore
if (existing == observer) if (existing == observer)
{ {
_localValueBindings?.Remove(property.Id); _localValueBindings?.Remove(property.Id);
ClearLocalValue(property); ClearValue(property);
} }
} }
} }
@ -616,11 +635,13 @@ namespace Avalonia.PropertyStore
{ {
object? value; object? value;
BindingPriority priority; BindingPriority priority;
bool overridden = false;
if (_effectiveValues.TryGetValue(property, out var v)) if (_effectiveValues.TryGetValue(property, out var v))
{ {
value = v.Value; value = v.Value;
priority = v.Priority; priority = v.Priority;
overridden = v.IsOverridenCurrentValue;
} }
else if (property.Inherits && TryGetInheritedValue(property, out v)) else if (property.Inherits && TryGetInheritedValue(property, out v))
{ {
@ -637,7 +658,8 @@ namespace Avalonia.PropertyStore
property, property,
value, value,
priority, priority,
null); null,
overridden);
} }
private int InsertFrame(ValueFrame frame) private int InsertFrame(ValueFrame frame)
@ -787,7 +809,7 @@ namespace Avalonia.PropertyStore
// - The value is a non-animation value and its priority is higher than the current // - The value is a non-animation value and its priority is higher than the current
// effective value's base priority // effective value's base priority
var isRelevantPriority = current is null || var isRelevantPriority = current is null ||
priority < current.Priority || (priority < current.Priority && priority < current.BasePriority) ||
(priority > BindingPriority.Animation && priority < current.BasePriority); (priority > BindingPriority.Animation && priority < current.BasePriority);
if (foundEntry && isRelevantPriority && entry!.HasValue) if (foundEntry && isRelevantPriority && entry!.HasValue)

4
src/Avalonia.Base/StyledElement.cs

@ -41,7 +41,11 @@ namespace Avalonia
public static readonly StyledProperty<object?> DataContextProperty = public static readonly StyledProperty<object?> DataContextProperty =
AvaloniaProperty.Register<StyledElement, object?>( AvaloniaProperty.Register<StyledElement, object?>(
nameof(DataContext), nameof(DataContext),
defaultValue: null,
inherits: true, inherits: true,
defaultBindingMode: BindingMode.OneWay,
validate: null,
coerce: null,
notifying: DataContextNotifying); notifying: DataContextNotifying);
/// <summary> /// <summary>

46
src/Avalonia.Base/StyledProperty.cs

@ -171,7 +171,7 @@ namespace Avalonia
internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o) internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o)
{ {
return new EffectiveValue<TValue>(o, this); return o.GetValueStore().CreateEffectiveValue(this);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -194,24 +194,48 @@ namespace Avalonia
} }
/// <inheritdoc/> /// <inheritdoc/>
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)]
internal override IDisposable? RouteSetValue( internal override IDisposable? RouteSetValue(
AvaloniaObject target, AvaloniaObject target,
object? value, object? value,
BindingPriority priority) BindingPriority priority)
{
if (ShouldSetValue(target, value, out var converted))
return target.SetValue<TValue>(this, converted, priority);
return null;
}
internal override void RouteSetCurrentValue(AvaloniaObject target, object? value)
{
if (ShouldSetValue(target, value, out var converted))
target.SetCurrentValue<TValue>(this, converted);
}
internal override IDisposable RouteBind(
AvaloniaObject target,
IObservable<object?> source,
BindingPriority priority)
{
return target.Bind<TValue>(this, source, priority);
}
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)]
private bool ShouldSetValue(AvaloniaObject target, object? value, [NotNullWhen(true)] out TValue? converted)
{ {
if (value == BindingOperations.DoNothing) if (value == BindingOperations.DoNothing)
{ {
return null; converted = default;
return false;
} }
else if (value == UnsetValue) if (value == UnsetValue)
{ {
target.ClearValue(this); target.ClearValue(this);
return null; converted = default;
return false;
} }
else if (TypeUtilities.TryConvertImplicit(PropertyType, value, out var converted)) else if (TypeUtilities.TryConvertImplicit(PropertyType, value, out var v))
{ {
return target.SetValue<TValue>(this, (TValue)converted!, priority); converted = (TValue)v!;
return true;
} }
else else
{ {
@ -220,14 +244,6 @@ namespace Avalonia
} }
} }
internal override IDisposable RouteBind(
AvaloniaObject target,
IObservable<object?> source,
BindingPriority priority)
{
return target.Bind<TValue>(this, source, priority);
}
private object? GetDefaultBoxedValue(Type type) private object? GetDefaultBoxedValue(Type type)
{ {
_ = type ?? throw new ArgumentNullException(nameof(type)); _ = type ?? throw new ArgumentNullException(nameof(type));

7
src/Avalonia.Controls/ItemsControl.cs

@ -559,7 +559,12 @@ namespace Avalonia.Controls
return new ItemContainerGenerator(this); return new ItemContainerGenerator(this);
} }
internal void AddLogicalChild(Control c) => LogicalChildren.Add(c); internal void AddLogicalChild(Control c)
{
if (!LogicalChildren.Contains(c))
LogicalChildren.Add(c);
}
internal void RemoveLogicalChild(Control c) => LogicalChildren.Remove(c); internal void RemoveLogicalChild(Control c) => LogicalChildren.Remove(c);
/// <summary> /// <summary>

33
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -63,6 +63,15 @@ namespace Avalonia.Controls.Presenters
o => o.PreeditText, o => o.PreeditText,
(o, v) => o.PreeditText = v); (o, v) => o.PreeditText = v);
/// <summary>
/// Defines the <see cref="CompositionRegion"/> property.
/// </summary>
public static readonly DirectProperty<TextPresenter, TextRange?> CompositionRegionProperty =
AvaloniaProperty.RegisterDirect<TextPresenter, TextRange?>(
nameof(CompositionRegion),
o => o.CompositionRegion,
(o, v) => o.CompositionRegion = v);
/// <summary> /// <summary>
/// Defines the <see cref="TextAlignment"/> property. /// Defines the <see cref="TextAlignment"/> property.
/// </summary> /// </summary>
@ -106,6 +115,7 @@ namespace Avalonia.Controls.Presenters
private Rect _caretBounds; private Rect _caretBounds;
private Point _navigationPosition; private Point _navigationPosition;
private string? _preeditText; private string? _preeditText;
private TextRange? _compositionRegion;
static TextPresenter() static TextPresenter()
{ {
@ -146,6 +156,12 @@ namespace Avalonia.Controls.Presenters
set => SetAndRaise(PreeditTextProperty, ref _preeditText, value); set => SetAndRaise(PreeditTextProperty, ref _preeditText, value);
} }
public TextRange? CompositionRegion
{
get => _compositionRegion;
set => SetAndRaise(CompositionRegionProperty, ref _compositionRegion, value);
}
/// <summary> /// <summary>
/// Gets or sets the font family. /// Gets or sets the font family.
/// </summary> /// </summary>
@ -548,7 +564,20 @@ namespace Avalonia.Controls.Presenters
var foreground = Foreground; var foreground = Foreground;
if (!string.IsNullOrEmpty(_preeditText)) if(_compositionRegion != null)
{
var preeditHighlight = new ValueSpan<TextRunProperties>(_compositionRegion?.Start ?? 0, _compositionRegion?.Length ?? 0,
new GenericTextRunProperties(typeface, FontSize,
foregroundBrush: foreground,
textDecorations: TextDecorations.Underline));
textStyleOverrides = new[]
{
preeditHighlight
};
}
else if (!string.IsNullOrEmpty(_preeditText))
{ {
var preeditHighlight = new ValueSpan<TextRunProperties>(_caretIndex, _preeditText.Length, var preeditHighlight = new ValueSpan<TextRunProperties>(_caretIndex, _preeditText.Length,
new GenericTextRunProperties(typeface, FontSize, new GenericTextRunProperties(typeface, FontSize,
@ -911,6 +940,7 @@ namespace Avalonia.Controls.Presenters
break; break;
} }
case nameof(CompositionRegion):
case nameof(Foreground): case nameof(Foreground):
case nameof(FontSize): case nameof(FontSize):
case nameof(FontStyle): case nameof(FontStyle):
@ -931,7 +961,6 @@ namespace Avalonia.Controls.Presenters
case nameof(PasswordChar): case nameof(PasswordChar):
case nameof(RevealPassword): case nameof(RevealPassword):
case nameof(FlowDirection): case nameof(FlowDirection):
{ {
InvalidateTextLayout(); InvalidateTextLayout();

117
src/Avalonia.Controls/TextBoxTextInputMethodClient.cs

@ -5,6 +5,7 @@ using Avalonia.Media.TextFormatting;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using static System.Net.Mime.MediaTypeNames;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -12,6 +13,7 @@ namespace Avalonia.Controls
{ {
private TextBox? _parent; private TextBox? _parent;
private TextPresenter? _presenter; private TextPresenter? _presenter;
private ITextEditable? _textEditable;
public Visual TextViewVisual => _presenter!; public Visual TextViewVisual => _presenter!;
@ -45,7 +47,7 @@ namespace Avalonia.Controls
{ {
get get
{ {
if(_presenter is null || _parent is null) if (_presenter is null || _parent is null)
{ {
return default; return default;
} }
@ -71,13 +73,70 @@ namespace Avalonia.Controls
} }
} }
public ITextEditable? TextEditable
{
get => _textEditable; set
{
if(_textEditable != null)
{
_textEditable.TextChanged -= TextEditable_TextChanged;
_textEditable.SelectionChanged -= TextEditable_SelectionChanged;
_textEditable.CompositionChanged -= TextEditable_CompositionChanged;
}
_textEditable = value;
if(_textEditable != null)
{
_textEditable.TextChanged += TextEditable_TextChanged;
_textEditable.SelectionChanged += TextEditable_SelectionChanged;
_textEditable.CompositionChanged += TextEditable_CompositionChanged;
if (_presenter != null)
{
_textEditable.Text = _presenter.Text;
_textEditable.SelectionStart = _presenter.SelectionStart;
_textEditable.SelectionEnd = _presenter.SelectionEnd;
}
}
}
}
private void TextEditable_CompositionChanged(object? sender, EventArgs e)
{
if (_presenter != null && _textEditable != null)
{
_presenter.CompositionRegion = new TextRange(_textEditable.CompositionStart, _textEditable.CompositionEnd);
}
}
private void TextEditable_SelectionChanged(object? sender, EventArgs e)
{
if(_parent != null && _textEditable != null)
{
_parent.SelectionStart = _textEditable.SelectionStart;
_parent.SelectionEnd = _textEditable.SelectionEnd;
}
}
private void TextEditable_TextChanged(object? sender, EventArgs e)
{
if (_parent != null)
{
if (_parent.Text != _textEditable?.Text)
{
_parent.Text = _textEditable?.Text;
}
}
}
private static string GetTextLineText(TextLine textLine) private static string GetTextLineText(TextLine textLine)
{ {
var builder = StringBuilderCache.Acquire(textLine.Length); var builder = StringBuilderCache.Acquire(textLine.Length);
foreach (var run in textLine.TextRuns) foreach (var run in textLine.TextRuns)
{ {
if(run.Length > 0) if (run.Length > 0)
{ {
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
builder.Append(run.Text.Span); builder.Append(run.Text.Span);
@ -110,9 +169,18 @@ namespace Avalonia.Controls
_presenter.PreeditText = text; _presenter.PreeditText = text;
} }
public void SetComposingRegion(TextRange? region)
{
if (_presenter == null)
{
return;
}
_presenter.CompositionRegion = region;
}
public void SelectInSurroundingText(int start, int end) public void SelectInSurroundingText(int start, int end)
{ {
if(_parent is null ||_presenter is null) if (_parent is null || _presenter is null)
{ {
return; return;
} }
@ -125,21 +193,21 @@ namespace Avalonia.Controls
var selectionStart = lineStart + start; var selectionStart = lineStart + start;
var selectionEnd = lineStart + end; var selectionEnd = lineStart + end;
_parent.SelectionStart = selectionStart; _parent.SelectionStart = selectionStart;
_parent.SelectionEnd = selectionEnd; _parent.SelectionEnd = selectionEnd;
} }
public void SetPresenter(TextPresenter? presenter, TextBox? parent) public void SetPresenter(TextPresenter? presenter, TextBox? parent)
{ {
if(_parent != null) if (_parent != null)
{ {
_parent.PropertyChanged -= OnParentPropertyChanged; _parent.PropertyChanged -= OnParentPropertyChanged;
} }
_parent = parent; _parent = parent;
if(_parent != null) if (_parent != null)
{ {
_parent.PropertyChanged += OnParentPropertyChanged; _parent.PropertyChanged += OnParentPropertyChanged;
} }
@ -148,16 +216,18 @@ namespace Avalonia.Controls
{ {
_presenter.PreeditText = null; _presenter.PreeditText = null;
_presenter.CaretBoundsChanged -= OnCaretBoundsChanged; _presenter.CompositionRegion = null;
_presenter.CaretBoundsChanged -= OnCaretBoundsChanged;
} }
_presenter = presenter; _presenter = presenter;
if (_presenter != null) if (_presenter != null)
{ {
_presenter.CaretBoundsChanged += OnCaretBoundsChanged; _presenter.CaretBoundsChanged += OnCaretBoundsChanged;
} }
TextViewVisualChanged?.Invoke(this, EventArgs.Empty); TextViewVisualChanged?.Invoke(this, EventArgs.Empty);
OnCaretBoundsChanged(this, EventArgs.Empty); OnCaretBoundsChanged(this, EventArgs.Empty);
@ -165,12 +235,33 @@ namespace Avalonia.Controls
private void OnParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) private void OnParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{ {
if(e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty) if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty)
{ {
if (SupportsSurroundingText) if (SupportsSurroundingText)
{ {
SurroundingTextChanged?.Invoke(this, e); SurroundingTextChanged?.Invoke(this, e);
} }
if (_textEditable != null)
{
var value = (int)(e.NewValue ?? 0);
if (e.Property == TextBox.SelectionStartProperty)
{
_textEditable.SelectionStart = value;
}
if (e.Property == TextBox.SelectionEndProperty)
{
_textEditable.SelectionEnd = value;
}
}
}
if(e.Property == TextBox.TextProperty)
{
if(_textEditable != null)
{
_textEditable.Text = (string?)e.NewValue;
}
} }
} }

2
src/Avalonia.FreeDesktop/DBusHelper.cs

@ -6,7 +6,7 @@ using Tmds.DBus;
namespace Avalonia.FreeDesktop namespace Avalonia.FreeDesktop
{ {
public static class DBusHelper internal static class DBusHelper
{ {
/// <summary> /// <summary>
/// This class uses synchronous execution at DBus connection establishment stage /// This class uses synchronous execution at DBus connection establishment stage

2
src/Avalonia.FreeDesktop/DBusIme/X11DBusImeHelper.cs

@ -6,7 +6,7 @@ using Tmds.DBus;
namespace Avalonia.FreeDesktop.DBusIme namespace Avalonia.FreeDesktop.DBusIme
{ {
public class X11DBusImeHelper internal class X11DBusImeHelper
{ {
private static readonly Dictionary<string, Func<Connection, IX11InputMethodFactory>> KnownMethods = private static readonly Dictionary<string, Func<Connection, IX11InputMethodFactory>> KnownMethods =
new Dictionary<string, Func<Connection, IX11InputMethodFactory>> new Dictionary<string, Func<Connection, IX11InputMethodFactory>>

2
src/Avalonia.FreeDesktop/DBusMenuExporter.cs

@ -15,7 +15,7 @@ using Tmds.DBus;
namespace Avalonia.FreeDesktop namespace Avalonia.FreeDesktop
{ {
public class DBusMenuExporter internal class DBusMenuExporter
{ {
public static ITopLevelNativeMenuExporter? TryCreateTopLevelNativeMenu(IntPtr xid) public static ITopLevelNativeMenuExporter? TryCreateTopLevelNativeMenu(IntPtr xid)
{ {

6
src/Avalonia.FreeDesktop/IX11InputMethod.cs

@ -6,13 +6,13 @@ using Avalonia.Input.TextInput;
namespace Avalonia.FreeDesktop namespace Avalonia.FreeDesktop
{ {
public interface IX11InputMethodFactory internal interface IX11InputMethodFactory
{ {
(ITextInputMethodImpl method, IX11InputMethodControl control) CreateClient(IntPtr xid); (ITextInputMethodImpl method, IX11InputMethodControl control) CreateClient(IntPtr xid);
} }
#pragma warning disable CA1815 // Override equals and operator equals on value types #pragma warning disable CA1815 // Override equals and operator equals on value types
public struct X11InputMethodForwardedKey internal struct X11InputMethodForwardedKey
#pragma warning restore CA1815 // Override equals and operator equals on value types #pragma warning restore CA1815 // Override equals and operator equals on value types
{ {
public int KeyVal { get; set; } public int KeyVal { get; set; }
@ -20,7 +20,7 @@ namespace Avalonia.FreeDesktop
public RawKeyEventType Type { get; set; } public RawKeyEventType Type { get; set; }
} }
public interface IX11InputMethodControl : IDisposable internal interface IX11InputMethodControl : IDisposable
{ {
void SetWindowActive(bool active); void SetWindowActive(bool active);
bool IsEnabled { get; } bool IsEnabled { get; }

2
src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoProvider.cs

@ -5,7 +5,7 @@ using Avalonia.Controls.Platform;
namespace Avalonia.FreeDesktop namespace Avalonia.FreeDesktop
{ {
public class LinuxMountedVolumeInfoProvider : IMountedVolumeInfoProvider internal class LinuxMountedVolumeInfoProvider : IMountedVolumeInfoProvider
{ {
public IDisposable Listen(ObservableCollection<MountedVolumeInfo> mountedDrives) public IDisposable Listen(ObservableCollection<MountedVolumeInfo> mountedDrives)
{ {

2
src/Avalonia.Headless/AvaloniaHeadlessPlatform.cs

@ -68,8 +68,6 @@ namespace Avalonia.Headless
.Bind<IKeyboardDevice>().ToConstant(new KeyboardDevice()) .Bind<IKeyboardDevice>().ToConstant(new KeyboardDevice())
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<IRenderTimer>().ToConstant(new RenderTimer(60)) .Bind<IRenderTimer>().ToConstant(new RenderTimer(60))
.Bind<IFontManagerImpl>().ToSingleton<HeadlessFontManagerStub>()
.Bind<ITextShaperImpl>().ToSingleton<HeadlessTextShaperStub>()
.Bind<IWindowingPlatform>().ToConstant(new HeadlessWindowingPlatform()) .Bind<IWindowingPlatform>().ToConstant(new HeadlessWindowingPlatform())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>(); .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), null); Compositor = new Compositor(AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), null);

2
src/Avalonia.Native/CallbackBase.cs

@ -6,7 +6,7 @@ using MicroCom.Runtime;
namespace Avalonia.Native namespace Avalonia.Native
{ {
public abstract class NativeCallbackBase : CallbackBase, IMicroComExceptionCallback internal abstract class NativeCallbackBase : CallbackBase, IMicroComExceptionCallback
{ {
public void RaiseException(Exception e) public void RaiseException(Exception e)
{ {

2
src/Avalonia.Native/MacOSMountedVolumeInfoProvider.cs

@ -64,7 +64,7 @@ namespace Avalonia.Native
} }
} }
public class MacOSMountedVolumeInfoProvider : IMountedVolumeInfoProvider internal class MacOSMountedVolumeInfoProvider : IMountedVolumeInfoProvider
{ {
public IDisposable Listen(ObservableCollection<MountedVolumeInfo> mountedDrives) public IDisposable Listen(ObservableCollection<MountedVolumeInfo> mountedDrives)
{ {

2
src/Avalonia.Native/MenuActionCallback.cs

@ -3,7 +3,7 @@ using Avalonia.Native.Interop;
namespace Avalonia.Native namespace Avalonia.Native
{ {
public class MenuActionCallback : NativeCallbackBase, IAvnActionCallback internal class MenuActionCallback : NativeCallbackBase, IAvnActionCallback
{ {
private Action _action; private Action _action;

2
src/Avalonia.Native/PredicateCallback.cs

@ -3,7 +3,7 @@ using Avalonia.Native.Interop;
namespace Avalonia.Native namespace Avalonia.Native
{ {
public class PredicateCallback : NativeCallbackBase, IAvnPredicateCallback internal class PredicateCallback : NativeCallbackBase, IAvnPredicateCallback
{ {
private Func<bool> _predicate; private Func<bool> _predicate;

2
src/Avalonia.Native/ScreenImpl.cs

@ -5,7 +5,7 @@ using Avalonia.Platform;
namespace Avalonia.Native namespace Avalonia.Native
{ {
class ScreenImpl : IScreenImpl, IDisposable internal class ScreenImpl : IScreenImpl, IDisposable
{ {
private IAvnScreens _native; private IAvnScreens _native;

2
src/Avalonia.X11/Glx/Glx.cs

@ -10,7 +10,7 @@ using Avalonia.SourceGenerator;
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
unsafe partial class GlxInterface internal unsafe partial class GlxInterface
{ {
private const string libGL = "libGL.so.1"; private const string libGL = "libGL.so.1";
[GetProcAddress("glXMakeContextCurrent")] [GetProcAddress("glXMakeContextCurrent")]

2
src/Avalonia.X11/Glx/GlxConsts.cs

@ -4,7 +4,7 @@
#pragma warning disable 414 #pragma warning disable 414
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
class GlxConsts internal class GlxConsts
{ {
public const int GLX_USE_GL = 1; public const int GLX_USE_GL = 1;
public const int GLX_BUFFER_SIZE = 2; public const int GLX_BUFFER_SIZE = 2;

6
src/Avalonia.X11/Glx/GlxContext.cs

@ -7,7 +7,7 @@ using Avalonia.Reactive;
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
class GlxContext : IGlContext internal class GlxContext : IGlContext
{ {
public IntPtr Handle { get; } public IntPtr Handle { get; }
public GlxInterface Glx { get; } public GlxInterface Glx { get; }
@ -46,8 +46,8 @@ namespace Avalonia.X11.Glx
public GlInterface GlInterface { get; } public GlInterface GlInterface { get; }
public int SampleCount { get; } public int SampleCount { get; }
public int StencilSize { get; } public int StencilSize { get; }
class RestoreContext : IDisposable private class RestoreContext : IDisposable
{ {
private GlxInterface _glx; private GlxInterface _glx;
private IntPtr _defaultDisplay; private IntPtr _defaultDisplay;

8
src/Avalonia.X11/Glx/GlxDisplay.cs

@ -6,7 +6,7 @@ using static Avalonia.X11.Glx.GlxConsts;
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
unsafe class GlxDisplay internal unsafe class GlxDisplay
{ {
private readonly X11Info _x11; private readonly X11Info _x11;
private readonly GlVersion[] _probeProfiles; private readonly GlVersion[] _probeProfiles;
@ -106,7 +106,7 @@ namespace Avalonia.X11.Glx
} }
} }
IntPtr CreatePBuffer() private IntPtr CreatePBuffer()
{ {
return Glx.CreatePbuffer(_x11.Display, _fbconfig, new[] { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 }); return Glx.CreatePbuffer(_x11.Display, _fbconfig, new[] { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 });
} }
@ -116,8 +116,8 @@ namespace Avalonia.X11.Glx
public GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share, public GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
share.SampleCount, share.StencilSize, true); share.SampleCount, share.StencilSize, true);
GlxContext CreateContext(IntPtr defaultXid, IGlContext share, private GlxContext CreateContext(IntPtr defaultXid, IGlContext share,
int sampleCount, int stencilSize, bool ownsPBuffer) int sampleCount, int stencilSize, bool ownsPBuffer)
{ {
var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero; var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero;

8
src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs

@ -6,7 +6,7 @@ using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
class GlxGlPlatformSurface: IGlPlatformSurface internal class GlxGlPlatformSurface: IGlPlatformSurface
{ {
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
@ -21,7 +21,7 @@ namespace Avalonia.X11.Glx
return new RenderTarget((GlxContext)context, _info); return new RenderTarget((GlxContext)context, _info);
} }
class RenderTarget : IGlPlatformSurfaceRenderTarget private class RenderTarget : IGlPlatformSurfaceRenderTarget
{ {
private readonly GlxContext _context; private readonly GlxContext _context;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
@ -46,8 +46,8 @@ namespace Avalonia.X11.Glx
return new Session(_context, _info, oldContext); return new Session(_context, _info, oldContext);
} }
class Session : IGlPlatformSurfaceRenderingSession private class Session : IGlPlatformSurfaceRenderingSession
{ {
private readonly GlxContext _context; private readonly GlxContext _context;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;

2
src/Avalonia.X11/Glx/GlxPlatformFeature.cs

@ -6,7 +6,7 @@ using Avalonia.Platform;
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
class GlxPlatformGraphics : IPlatformGraphics internal class GlxPlatformGraphics : IPlatformGraphics
{ {
public GlxDisplay Display { get; private set; } public GlxDisplay Display { get; private set; }
public bool CanCreateContexts => true; public bool CanCreateContexts => true;

2
src/Avalonia.X11/Keysyms.cs

@ -3,7 +3,7 @@
// ReSharper disable CommentTypo // ReSharper disable CommentTypo
namespace Avalonia.X11 namespace Avalonia.X11
{ {
enum X11Key internal enum X11Key
{ {
VoidSymbol = 0xffffff /* Void symbol */, VoidSymbol = 0xffffff /* Void symbol */,
BackSpace = 0xff08 /* Back space, back char */, BackSpace = 0xff08 /* Back space, back char */,

29
src/Avalonia.X11/NativeDialogs/Gtk.cs

@ -7,8 +7,7 @@ using Avalonia.Platform.Interop;
// ReSharper disable IdentifierTypo // ReSharper disable IdentifierTypo
namespace Avalonia.X11.NativeDialogs namespace Avalonia.X11.NativeDialogs
{ {
internal static unsafe class Glib
static unsafe class Glib
{ {
private const string GlibName = "libglib-2.0.so.0"; private const string GlibName = "libglib-2.0.so.0";
private const string GObjectName = "libgobject-2.0.so.0"; private const string GObjectName = "libgobject-2.0.so.0";
@ -36,7 +35,7 @@ namespace Avalonia.X11.NativeDialogs
IntPtr destroy); IntPtr destroy);
class ConnectedSignal : IDisposable private class ConnectedSignal : IDisposable
{ {
private readonly IntPtr _instance; private readonly IntPtr _instance;
private GCHandle _handle; private GCHandle _handle;
@ -75,7 +74,7 @@ namespace Avalonia.X11.NativeDialogs
} }
static bool TimeoutHandler(IntPtr data) private static bool TimeoutHandler(IntPtr data)
{ {
var handle = GCHandle.FromIntPtr(data); var handle = GCHandle.FromIntPtr(data);
var cb = (Func<bool>)handle.Target; var cb = (Func<bool>)handle.Target;
@ -95,7 +94,7 @@ namespace Avalonia.X11.NativeDialogs
s_pinnedHandler = TimeoutHandler; s_pinnedHandler = TimeoutHandler;
} }
static void AddTimeout(int priority, uint interval, Func<bool> callback) private static void AddTimeout(int priority, uint interval, Func<bool> callback)
{ {
var handle = GCHandle.Alloc(callback); var handle = GCHandle.Alloc(callback);
g_timeout_add_full(priority, interval, s_pinnedHandler, GCHandle.ToIntPtr(handle), IntPtr.Zero); g_timeout_add_full(priority, interval, s_pinnedHandler, GCHandle.ToIntPtr(handle), IntPtr.Zero);
@ -123,13 +122,13 @@ namespace Avalonia.X11.NativeDialogs
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct GSList internal unsafe struct GSList
{ {
public readonly IntPtr Data; public readonly IntPtr Data;
public readonly GSList* Next; public readonly GSList* Next;
} }
enum GtkFileChooserAction internal enum GtkFileChooserAction
{ {
Open, Open,
Save, Save,
@ -137,7 +136,7 @@ namespace Avalonia.X11.NativeDialogs
} }
// ReSharper disable UnusedMember.Global // ReSharper disable UnusedMember.Global
enum GtkResponseType internal enum GtkResponseType
{ {
Help = -11, Help = -11,
Apply = -10, Apply = -10,
@ -153,14 +152,14 @@ namespace Avalonia.X11.NativeDialogs
} }
// ReSharper restore UnusedMember.Global // ReSharper restore UnusedMember.Global
static unsafe class Gtk internal static unsafe class Gtk
{ {
private static IntPtr s_display; private static IntPtr s_display;
private const string GdkName = "libgdk-3.so.0"; private const string GdkName = "libgdk-3.so.0";
private const string GtkName = "libgtk-3.so.0"; private const string GtkName = "libgtk-3.so.0";
[DllImport(GtkName)] [DllImport(GtkName)]
static extern void gtk_main_iteration(); private static extern void gtk_main_iteration();
[DllImport(GtkName)] [DllImport(GtkName)]
@ -231,10 +230,10 @@ namespace Avalonia.X11.NativeDialogs
public static extern void gtk_widget_hide(IntPtr gtkWidget); public static extern void gtk_widget_hide(IntPtr gtkWidget);
[DllImport(GtkName)] [DllImport(GtkName)]
static extern bool gtk_init_check(int argc, IntPtr argv); private static extern bool gtk_init_check(int argc, IntPtr argv);
[DllImport(GdkName)] [DllImport(GdkName)]
static extern IntPtr gdk_x11_window_foreign_new_for_display(IntPtr display, IntPtr xid); private static extern IntPtr gdk_x11_window_foreign_new_for_display(IntPtr display, IntPtr xid);
[DllImport(GdkName)] [DllImport(GdkName)]
public static extern IntPtr gdk_x11_window_get_xid(IntPtr window); public static extern IntPtr gdk_x11_window_get_xid(IntPtr window);
@ -244,13 +243,13 @@ namespace Avalonia.X11.NativeDialogs
public static extern IntPtr gtk_container_add(IntPtr container, IntPtr widget); public static extern IntPtr gtk_container_add(IntPtr container, IntPtr widget);
[DllImport(GdkName)] [DllImport(GdkName)]
static extern IntPtr gdk_set_allowed_backends(Utf8Buffer backends); private static extern IntPtr gdk_set_allowed_backends(Utf8Buffer backends);
[DllImport(GdkName)] [DllImport(GdkName)]
static extern IntPtr gdk_display_get_default(); private static extern IntPtr gdk_display_get_default();
[DllImport(GtkName)] [DllImport(GtkName)]
static extern IntPtr gtk_application_new(Utf8Buffer appId, int flags); private static extern IntPtr gtk_application_new(Utf8Buffer appId, int flags);
[DllImport(GdkName)] [DllImport(GdkName)]
public static extern void gdk_window_set_transient_for(IntPtr window, IntPtr parent); public static extern void gdk_window_set_transient_for(IntPtr window, IntPtr parent);

2
src/Avalonia.X11/TransparencyHelper.cs

@ -3,7 +3,7 @@ using Avalonia.Controls;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
class TransparencyHelper : IDisposable, X11Globals.IGlobalsSubscriber internal class TransparencyHelper : IDisposable, X11Globals.IGlobalsSubscriber
{ {
private readonly X11Info _x11; private readonly X11Info _x11;
private readonly IntPtr _window; private readonly IntPtr _window;

16
src/Avalonia.X11/X11Clipboard.cs

@ -9,7 +9,7 @@ using Avalonia.Input.Platform;
using static Avalonia.X11.XLib; using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
class X11Clipboard : IClipboard internal class X11Clipboard : IClipboard
{ {
private readonly X11Info _x11; private readonly X11Info _x11;
private IDataObject _storedDataObject; private IDataObject _storedDataObject;
@ -33,12 +33,12 @@ namespace Avalonia.X11
}.Where(a => a != IntPtr.Zero).ToArray(); }.Where(a => a != IntPtr.Zero).ToArray();
} }
bool IsStringAtom(IntPtr atom) private bool IsStringAtom(IntPtr atom)
{ {
return _textAtoms.Contains(atom); return _textAtoms.Contains(atom);
} }
Encoding GetStringEncoding(IntPtr atom) private Encoding GetStringEncoding(IntPtr atom)
{ {
return (atom == _x11.Atoms.XA_STRING return (atom == _x11.Atoms.XA_STRING
|| atom == _x11.Atoms.OEMTEXT) || atom == _x11.Atoms.OEMTEXT)
@ -213,7 +213,7 @@ namespace Avalonia.X11
} }
} }
Task<IntPtr[]> SendFormatRequest() private Task<IntPtr[]> SendFormatRequest()
{ {
if (_requestedFormatsTcs == null || _requestedFormatsTcs.Task.IsCompleted) if (_requestedFormatsTcs == null || _requestedFormatsTcs.Task.IsCompleted)
_requestedFormatsTcs = new TaskCompletionSource<IntPtr[]>(); _requestedFormatsTcs = new TaskCompletionSource<IntPtr[]>();
@ -222,7 +222,7 @@ namespace Avalonia.X11
return _requestedFormatsTcs.Task; return _requestedFormatsTcs.Task;
} }
Task<object> SendDataRequest(IntPtr format) private Task<object> SendDataRequest(IntPtr format)
{ {
if (_requestedDataTcs == null || _requestedFormatsTcs.Task.IsCompleted) if (_requestedDataTcs == null || _requestedFormatsTcs.Task.IsCompleted)
_requestedDataTcs = new TaskCompletionSource<object>(); _requestedDataTcs = new TaskCompletionSource<object>();
@ -230,7 +230,7 @@ namespace Avalonia.X11
return _requestedDataTcs.Task; return _requestedDataTcs.Task;
} }
bool HasOwner => XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD) != IntPtr.Zero; private bool HasOwner => XGetSelectionOwner(_x11.Display, _x11.Atoms.CLIPBOARD) != IntPtr.Zero;
public async Task<string> GetTextAsync() public async Task<string> GetTextAsync()
{ {
@ -252,7 +252,7 @@ namespace Avalonia.X11
return (string)await SendDataRequest(target); return (string)await SendDataRequest(target);
} }
void StoreAtomsInClipboardManager(IntPtr[] atoms) private void StoreAtomsInClipboardManager(IntPtr[] atoms)
{ {
if (_x11.Atoms.CLIPBOARD_MANAGER != IntPtr.Zero && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero) if (_x11.Atoms.CLIPBOARD_MANAGER != IntPtr.Zero && _x11.Atoms.SAVE_TARGETS != IntPtr.Zero)
{ {

4
src/Avalonia.X11/X11CursorFactory.cs

@ -12,7 +12,7 @@ using Avalonia.Utilities;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
partial class X11CursorFactory : ICursorFactory internal partial class X11CursorFactory : ICursorFactory
{ {
private static readonly byte[] NullCursorData = new byte[] { 0 }; private static readonly byte[] NullCursorData = new byte[] { 0 };
@ -142,7 +142,7 @@ namespace Avalonia.X11
} }
} }
class CursorImpl : ICursorImpl internal class CursorImpl : ICursorImpl
{ {
public CursorImpl() { } public CursorImpl() { }
public CursorImpl(IntPtr handle) => Handle = handle; public CursorImpl(IntPtr handle) => Handle = handle;

8
src/Avalonia.X11/X11Enums.cs

@ -3,7 +3,7 @@ using System;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
public enum Status internal enum Status
{ {
Success = 0, /* everything's okay */ Success = 0, /* everything's okay */
BadRequest = 1, /* bad request code */ BadRequest = 1, /* bad request code */
@ -38,7 +38,7 @@ namespace Avalonia.X11
} }
[Flags] [Flags]
public enum XEventMask : int internal enum XEventMask : int
{ {
NoEventMask = 0, NoEventMask = 0,
KeyPressMask = (1 << 0), KeyPressMask = (1 << 0),
@ -69,7 +69,7 @@ namespace Avalonia.X11
} }
[Flags] [Flags]
public enum XModifierMask internal enum XModifierMask
{ {
ShiftMask = (1 << 0), ShiftMask = (1 << 0),
LockMask = (1 << 1), LockMask = (1 << 1),
@ -89,7 +89,7 @@ namespace Avalonia.X11
} }
[Flags] [Flags]
public enum XCreateWindowFlags internal enum XCreateWindowFlags
{ {
CWBackPixmap = (1 << 0), CWBackPixmap = (1 << 0),
CWBackPixel = (1 << 1), CWBackPixel = (1 << 1),

2
src/Avalonia.X11/X11Exception.cs

@ -2,7 +2,7 @@ using System;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
public class X11Exception : Exception internal class X11Exception : Exception
{ {
public X11Exception(string message) : base(message) public X11Exception(string message) : base(message)
{ {

2
src/Avalonia.X11/X11Framebuffer.cs

@ -5,7 +5,7 @@ using SkiaSharp;
using static Avalonia.X11.XLib; using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
class X11Framebuffer : ILockedFramebuffer internal class X11Framebuffer : ILockedFramebuffer
{ {
private readonly IntPtr _display; private readonly IntPtr _display;
private readonly IntPtr _xid; private readonly IntPtr _xid;

2
src/Avalonia.X11/X11FramebufferSurface.cs

@ -4,7 +4,7 @@ using Avalonia.Platform;
using static Avalonia.X11.XLib; using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
public class X11FramebufferSurface : IFramebufferPlatformSurface internal class X11FramebufferSurface : IFramebufferPlatformSurface
{ {
private readonly IntPtr _display; private readonly IntPtr _display;
private readonly IntPtr _xid; private readonly IntPtr _xid;

12
src/Avalonia.X11/X11Globals.cs

@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
using static Avalonia.X11.XLib; using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
unsafe class X11Globals internal unsafe class X11Globals
{ {
private readonly AvaloniaX11Platform _plat; private readonly AvaloniaX11Platform _plat;
private readonly int _screenNumber; private readonly int _screenNumber;
@ -75,7 +75,7 @@ namespace Avalonia.X11
} }
} }
IntPtr GetSupportingWmCheck(IntPtr window) private IntPtr GetSupportingWmCheck(IntPtr window)
{ {
XGetWindowProperty(_x11.Display, _rootWindow, _x11.Atoms._NET_SUPPORTING_WM_CHECK, XGetWindowProperty(_x11.Display, _rootWindow, _x11.Atoms._NET_SUPPORTING_WM_CHECK,
IntPtr.Zero, new IntPtr(IntPtr.Size), false, IntPtr.Zero, new IntPtr(IntPtr.Size), false,
@ -95,7 +95,7 @@ namespace Avalonia.X11
} }
} }
void UpdateCompositingAtomOwner() private void UpdateCompositingAtomOwner()
{ {
// This procedure is described in https://tronche.com/gui/x/icccm/sec-2.html#s-2.8 // This procedure is described in https://tronche.com/gui/x/icccm/sec-2.html#s-2.8
@ -128,10 +128,10 @@ namespace Avalonia.X11
if(ev.type == XEventName.DestroyNotify) if(ev.type == XEventName.DestroyNotify)
UpdateCompositingAtomOwner(); UpdateCompositingAtomOwner();
} }
void UpdateWmName() => WmName = GetWmName();
string GetWmName() private void UpdateWmName() => WmName = GetWmName();
private string GetWmName()
{ {
var wm = GetSupportingWmCheck(_rootWindow); var wm = GetSupportingWmCheck(_rootWindow);
if (wm == IntPtr.Zero || wm != GetSupportingWmCheck(wm)) if (wm == IntPtr.Zero || wm != GetSupportingWmCheck(wm))

8
src/Avalonia.X11/X11IconLoader.cs

@ -7,9 +7,9 @@ using Avalonia.Platform;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
class X11IconLoader : IPlatformIconLoader internal class X11IconLoader : IPlatformIconLoader
{ {
static IWindowIconImpl LoadIcon(Bitmap bitmap) private static IWindowIconImpl LoadIcon(Bitmap bitmap)
{ {
var rv = new X11IconData(bitmap); var rv = new X11IconData(bitmap);
bitmap.Dispose(); bitmap.Dispose();
@ -28,8 +28,8 @@ namespace Avalonia.X11
return LoadIcon(ms); return LoadIcon(ms);
} }
} }
unsafe class X11IconData : IWindowIconImpl, IFramebufferPlatformSurface internal unsafe class X11IconData : IWindowIconImpl, IFramebufferPlatformSurface
{ {
private int _width; private int _width;
private int _height; private int _height;

2
src/Avalonia.X11/X11Info.cs

@ -6,7 +6,7 @@ using static Avalonia.X11.XLib;
// ReSharper disable UnusedAutoPropertyAccessor.Local // ReSharper disable UnusedAutoPropertyAccessor.Local
namespace Avalonia.X11 namespace Avalonia.X11
{ {
unsafe class X11Info internal unsafe class X11Info
{ {
public IntPtr Display { get; } public IntPtr Display { get; }
public IntPtr DeferredDisplay { get; } public IntPtr DeferredDisplay { get; }

2
src/Avalonia.X11/X11KeyTransform.cs

@ -4,7 +4,7 @@ using Avalonia.Input;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
static class X11KeyTransform internal static class X11KeyTransform
{ {
private static readonly Dictionary<X11Key, Key> KeyDic = new Dictionary<X11Key, Key> private static readonly Dictionary<X11Key, Key> KeyDic = new Dictionary<X11Key, Key>
{ {

10
src/Avalonia.X11/X11NativeControlHost.cs

@ -6,7 +6,7 @@ using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
// TODO: Actually implement XEmbed instead of simply using XReparentWindow // TODO: Actually implement XEmbed instead of simply using XReparentWindow
class X11NativeControlHost : INativeControlHostImpl internal class X11NativeControlHost : INativeControlHostImpl
{ {
private readonly AvaloniaX11Platform _platform; private readonly AvaloniaX11Platform _platform;
public X11Window Window { get; } public X11Window Window { get; }
@ -57,7 +57,7 @@ namespace Avalonia.X11
public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == "XID"; public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == "XID";
class DumbWindow : INativeControlHostDestroyableControlHandle private class DumbWindow : INativeControlHostDestroyableControlHandle
{ {
private readonly IntPtr _display; private readonly IntPtr _display;
@ -96,8 +96,8 @@ namespace Avalonia.X11
} }
} }
} }
class Attachment : INativeControlHostControlTopLevelAttachment private class Attachment : INativeControlHostControlTopLevelAttachment
{ {
private readonly IntPtr _display; private readonly IntPtr _display;
private readonly IntPtr _orphanedWindow; private readonly IntPtr _orphanedWindow;
@ -129,7 +129,7 @@ namespace Avalonia.X11
_attachedTo = null; _attachedTo = null;
} }
void CheckDisposed() private void CheckDisposed()
{ {
if (_child == null) if (_child == null)
throw new ObjectDisposedException("X11 INativeControlHostControlTopLevelAttachment"); throw new ObjectDisposedException("X11 INativeControlHostControlTopLevelAttachment");

8
src/Avalonia.X11/X11Platform.cs

@ -20,7 +20,7 @@ using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
class AvaloniaX11Platform : IWindowingPlatform internal class AvaloniaX11Platform : IWindowingPlatform
{ {
private Lazy<KeyboardDevice> _keyboardDevice = new Lazy<KeyboardDevice>(() => new KeyboardDevice()); private Lazy<KeyboardDevice> _keyboardDevice = new Lazy<KeyboardDevice>(() => new KeyboardDevice());
public KeyboardDevice KeyboardDevice => _keyboardDevice.Value; public KeyboardDevice KeyboardDevice => _keyboardDevice.Value;
@ -35,7 +35,7 @@ namespace Avalonia.X11
public IntPtr OrphanedWindow { get; private set; } public IntPtr OrphanedWindow { get; private set; }
public X11Globals Globals { get; private set; } public X11Globals Globals { get; private set; }
[DllImport("libc")] [DllImport("libc")]
static extern void setlocale(int type, string s); private static extern void setlocale(int type, string s);
public void Initialize(X11PlatformOptions options) public void Initialize(X11PlatformOptions options)
{ {
Options = options; Options = options;
@ -138,7 +138,7 @@ namespace Avalonia.X11
throw new NotSupportedException(); throw new NotSupportedException();
} }
static bool EnableIme(X11PlatformOptions options) private static bool EnableIme(X11PlatformOptions options)
{ {
// Disable if explicitly asked by user // Disable if explicitly asked by user
var avaloniaImModule = Environment.GetEnvironmentVariable("AVALONIA_IM_MODULE"); var avaloniaImModule = Environment.GetEnvironmentVariable("AVALONIA_IM_MODULE");
@ -160,7 +160,7 @@ namespace Avalonia.X11
return isCjkLocale; return isCjkLocale;
} }
static bool ShouldUseXim() private static bool ShouldUseXim()
{ {
// Check if we are forbidden from using IME // Check if we are forbidden from using IME
if (Environment.GetEnvironmentVariable("AVALONIA_IM_MODULE") == "none" if (Environment.GetEnvironmentVariable("AVALONIA_IM_MODULE") == "none"

32
src/Avalonia.X11/X11PlatformThreading.cs

@ -9,7 +9,7 @@ using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
unsafe class X11PlatformThreading : IPlatformThreadingInterface internal unsafe class X11PlatformThreading : IPlatformThreadingInterface
{ {
private readonly AvaloniaX11Platform _platform; private readonly AvaloniaX11Platform _platform;
private readonly IntPtr _display; private readonly IntPtr _display;
@ -19,7 +19,7 @@ namespace Avalonia.X11
private Thread _mainThread; private Thread _mainThread;
[StructLayout(LayoutKind.Explicit)] [StructLayout(LayoutKind.Explicit)]
struct epoll_data private struct epoll_data
{ {
[FieldOffset(0)] [FieldOffset(0)]
public IntPtr ptr; public IntPtr ptr;
@ -36,30 +36,30 @@ namespace Avalonia.X11
private const int O_NONBLOCK = 2048; private const int O_NONBLOCK = 2048;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct epoll_event private struct epoll_event
{ {
public uint events; public uint events;
public epoll_data data; public epoll_data data;
} }
[DllImport("libc")] [DllImport("libc")]
extern static int epoll_create1(int size); private extern static int epoll_create1(int size);
[DllImport("libc")] [DllImport("libc")]
extern static int epoll_ctl(int epfd, int op, int fd, ref epoll_event __event); private extern static int epoll_ctl(int epfd, int op, int fd, ref epoll_event __event);
[DllImport("libc")] [DllImport("libc")]
extern static int epoll_wait(int epfd, epoll_event* events, int maxevents, int timeout); private extern static int epoll_wait(int epfd, epoll_event* events, int maxevents, int timeout);
[DllImport("libc")] [DllImport("libc")]
extern static int pipe2(int* fds, int flags); private extern static int pipe2(int* fds, int flags);
[DllImport("libc")] [DllImport("libc")]
extern static IntPtr write(int fd, void* buf, IntPtr count); private extern static IntPtr write(int fd, void* buf, IntPtr count);
[DllImport("libc")] [DllImport("libc")]
extern static IntPtr read(int fd, void* buf, IntPtr count); private extern static IntPtr read(int fd, void* buf, IntPtr count);
enum EventCodes private enum EventCodes
{ {
X11 = 1, X11 = 1,
Signal =2 Signal =2
@ -72,7 +72,7 @@ namespace Avalonia.X11
private int _epoll; private int _epoll;
private Stopwatch _clock = Stopwatch.StartNew(); private Stopwatch _clock = Stopwatch.StartNew();
class X11Timer : IDisposable private class X11Timer : IDisposable
{ {
private readonly X11PlatformThreading _parent; private readonly X11PlatformThreading _parent;
@ -104,7 +104,7 @@ namespace Avalonia.X11
} }
} }
List<X11Timer> _timers = new List<X11Timer>(); private List<X11Timer> _timers = new List<X11Timer>();
public X11PlatformThreading(AvaloniaX11Platform platform) public X11PlatformThreading(AvaloniaX11Platform platform)
{ {
@ -139,12 +139,12 @@ namespace Avalonia.X11
throw new X11Exception("Unable to attach signal pipe to epoll"); throw new X11Exception("Unable to attach signal pipe to epoll");
} }
int TimerComparer(X11Timer t1, X11Timer t2) private int TimerComparer(X11Timer t1, X11Timer t2)
{ {
return t2.Priority - t1.Priority; return t2.Priority - t1.Priority;
} }
void CheckSignaled() private void CheckSignaled()
{ {
int buf = 0; int buf = 0;
while (read(_sigread, &buf, new IntPtr(4)).ToInt64() > 0) while (read(_sigread, &buf, new IntPtr(4)).ToInt64() > 0)
@ -164,7 +164,7 @@ namespace Avalonia.X11
Signaled?.Invoke(prio); Signaled?.Invoke(prio);
} }
unsafe void HandleX11(CancellationToken cancellationToken) private unsafe void HandleX11(CancellationToken cancellationToken)
{ {
while (XPending(_display) != 0) while (XPending(_display) != 0)
{ {

20
src/Avalonia.X11/X11Screens.cs

@ -8,7 +8,7 @@ using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
class X11Screens : IScreenImpl internal class X11Screens : IScreenImpl
{ {
private IX11Screens _impl; private IX11Screens _impl;
@ -17,7 +17,7 @@ namespace Avalonia.X11
_impl = impl; _impl = impl;
} }
static unsafe X11Screen[] UpdateWorkArea(X11Info info, X11Screen[] screens) private static unsafe X11Screen[] UpdateWorkArea(X11Info info, X11Screen[] screens)
{ {
var rect = default(PixelRect); var rect = default(PixelRect);
foreach (var s in screens) foreach (var s in screens)
@ -58,14 +58,14 @@ namespace Avalonia.X11
XFree(prop); XFree(prop);
return screens; return screens;
} }
class Randr15ScreensImpl : IX11Screens private class Randr15ScreensImpl : IX11Screens
{ {
private readonly X11ScreensUserSettings _settings; private readonly X11ScreensUserSettings _settings;
private X11Screen[] _cache; private X11Screen[] _cache;
private X11Info _x11; private X11Info _x11;
private IntPtr _window; private IntPtr _window;
const int EDIDStructureLength = 32; // Length of a EDID-Block-Length(128 bytes), XRRGetOutputProperty multiplies offset and length by 4 private const int EDIDStructureLength = 32; // Length of a EDID-Block-Length(128 bytes), XRRGetOutputProperty multiplies offset and length by 4
public Randr15ScreensImpl(AvaloniaX11Platform platform, X11ScreensUserSettings settings) public Randr15ScreensImpl(AvaloniaX11Platform platform, X11ScreensUserSettings settings)
{ {
@ -160,7 +160,7 @@ namespace Avalonia.X11
} }
} }
class FallbackScreensImpl : IX11Screens private class FallbackScreensImpl : IX11Screens
{ {
public FallbackScreensImpl(X11Info info, X11ScreensUserSettings settings) public FallbackScreensImpl(X11Info info, X11ScreensUserSettings settings)
{ {
@ -220,17 +220,17 @@ namespace Avalonia.X11
_impl.Screens.Select(s => new Screen(s.Scaling, s.Bounds, s.WorkingArea, s.IsPrimary)).ToArray(); _impl.Screens.Select(s => new Screen(s.Scaling, s.Bounds, s.WorkingArea, s.IsPrimary)).ToArray();
} }
interface IX11Screens internal interface IX11Screens
{ {
X11Screen[] Screens { get; } X11Screen[] Screens { get; }
} }
class X11ScreensUserSettings internal class X11ScreensUserSettings
{ {
public double GlobalScaleFactor { get; set; } = 1; public double GlobalScaleFactor { get; set; } = 1;
public Dictionary<string, double> NamedScaleFactors { get; set; } public Dictionary<string, double> NamedScaleFactors { get; set; }
static double? TryParse(string s) private static double? TryParse(string s)
{ {
if (s == null) if (s == null)
return null; return null;
@ -276,7 +276,7 @@ namespace Avalonia.X11
} }
} }
class X11Screen internal class X11Screen
{ {
private const int FullHDWidth = 1920; private const int FullHDWidth = 1920;
private const int FullHDHeight = 1080; private const int FullHDHeight = 1080;

9
src/Avalonia.X11/X11Structs.cs

@ -1751,8 +1751,7 @@ namespace Avalonia.X11 {
{ {
public IntPtr client_data; public IntPtr client_data;
public XIMProc callback; public XIMProc callback;
[NonSerialized] [NonSerialized] private GCHandle gch;
GCHandle gch;
public XIMCallback (IntPtr clientData, XIMProc proc) public XIMCallback (IntPtr clientData, XIMProc proc)
{ {
@ -1769,7 +1768,7 @@ namespace Avalonia.X11 {
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
#pragma warning disable CA1815 // Override equals and operator equals on value types #pragma warning disable CA1815 // Override equals and operator equals on value types
public unsafe struct XImage internal unsafe struct XImage
#pragma warning restore CA1815 // Override equals and operator equals on value types #pragma warning restore CA1815 // Override equals and operator equals on value types
{ {
public int width, height; /* size of image */ public int width, height; /* size of image */
@ -1896,8 +1895,8 @@ namespace Avalonia.X11 {
public const string XNSpotLocation = "spotLocation"; public const string XNSpotLocation = "spotLocation";
public const string XNFontSet = "fontSet"; public const string XNFontSet = "fontSet";
} }
unsafe struct XRRMonitorInfo { internal unsafe struct XRRMonitorInfo {
public IntPtr Name; public IntPtr Name;
public int Primary; public int Primary;
public int Automatic; public int Automatic;

32
src/Avalonia.X11/X11Window.Ime.cs

@ -11,7 +11,7 @@ using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
partial class X11Window internal partial class X11Window
{ {
private ITextInputMethodImpl _ime; private ITextInputMethodImpl _ime;
private IX11InputMethodControl _imeControl; private IX11InputMethodControl _imeControl;
@ -20,7 +20,7 @@ namespace Avalonia.X11
private Queue<(RawKeyEventArgs args, XEvent xev, int keyval, int keycode)> _imeQueue = private Queue<(RawKeyEventArgs args, XEvent xev, int keyval, int keycode)> _imeQueue =
new Queue<(RawKeyEventArgs args, XEvent xev, int keyVal, int keyCode)>(); new Queue<(RawKeyEventArgs args, XEvent xev, int keyVal, int keyCode)>();
unsafe void CreateIC() private unsafe void CreateIC()
{ {
if (_x11.HasXim) if (_x11.HasXim)
{ {
@ -65,8 +65,8 @@ namespace Avalonia.X11
new IntPtr((int)(XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing)), new IntPtr((int)(XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing)),
XNames.XNClientWindow, _handle, XNames.XNFocusWindow, _handle, IntPtr.Zero); XNames.XNClientWindow, _handle, XNames.XNFocusWindow, _handle, IntPtr.Zero);
} }
void InitializeIme() private void InitializeIme()
{ {
var ime = AvaloniaLocator.Current.GetService<IX11InputMethodFactory>()?.CreateClient(_handle); var ime = AvaloniaLocator.Current.GetService<IX11InputMethodFactory>()?.CreateClient(_handle);
if (ime == null && _x11.HasXim) if (ime == null && _x11.HasXim)
@ -89,9 +89,9 @@ namespace Avalonia.X11
} }
} }
void UpdateImePosition() => _imeControl?.UpdateWindowInfo(Position, RenderScaling); private void UpdateImePosition() => _imeControl?.UpdateWindowInfo(Position, RenderScaling);
void HandleKeyEvent(ref XEvent ev) private void HandleKeyEvent(ref XEvent ev)
{ {
var index = ev.KeyEvent.state.HasAllFlags(XModifierMask.ShiftMask); var index = ev.KeyEvent.state.HasAllFlags(XModifierMask.ShiftMask);
@ -117,7 +117,7 @@ namespace Avalonia.X11
ScheduleKeyInput(args, ref ev, (int)key, ev.KeyEvent.keycode); ScheduleKeyInput(args, ref ev, (int)key, ev.KeyEvent.keycode);
} }
void TriggerClassicTextInputEvent(ref XEvent ev) private void TriggerClassicTextInputEvent(ref XEvent ev)
{ {
var text = TranslateEventToString(ref ev); var text = TranslateEventToString(ref ev);
if (text != null) if (text != null)
@ -128,8 +128,8 @@ namespace Avalonia.X11
private const int ImeBufferSize = 64 * 1024; private const int ImeBufferSize = 64 * 1024;
[ThreadStatic] private static IntPtr ImeBuffer; [ThreadStatic] private static IntPtr ImeBuffer;
unsafe string TranslateEventToString(ref XEvent ev) private unsafe string TranslateEventToString(ref XEvent ev)
{ {
if (ImeBuffer == IntPtr.Zero) if (ImeBuffer == IntPtr.Zero)
ImeBuffer = Marshal.AllocHGlobal(ImeBufferSize); ImeBuffer = Marshal.AllocHGlobal(ImeBufferSize);
@ -158,9 +158,9 @@ namespace Avalonia.X11
return text; return text;
} }
void ScheduleKeyInput(RawKeyEventArgs args, ref XEvent xev, int keyval, int keycode) private void ScheduleKeyInput(RawKeyEventArgs args, ref XEvent xev, int keyval, int keycode)
{ {
_x11.LastActivityTimestamp = xev.ButtonEvent.time; _x11.LastActivityTimestamp = xev.ButtonEvent.time;
@ -170,8 +170,8 @@ namespace Avalonia.X11
ScheduleInput(args); ScheduleInput(args);
} }
bool FilterIme(RawKeyEventArgs args, XEvent xev, int keyval, int keycode) private bool FilterIme(RawKeyEventArgs args, XEvent xev, int keyval, int keycode)
{ {
if (_ime == null) if (_ime == null)
return false; return false;
@ -182,7 +182,7 @@ namespace Avalonia.X11
return true; return true;
} }
async void ProcessNextImeEvent() private async void ProcessNextImeEvent()
{ {
if(_processingIme) if(_processingIme)
return; return;
@ -203,7 +203,7 @@ namespace Avalonia.X11
} }
// This class is used to attach the text value of the key to an asynchronously dispatched KeyDown event // This class is used to attach the text value of the key to an asynchronously dispatched KeyDown event
class RawKeyEventArgsWithText : RawKeyEventArgs private class RawKeyEventArgsWithText : RawKeyEventArgs
{ {
public RawKeyEventArgsWithText(IKeyboardDevice device, ulong timestamp, IInputRoot root, public RawKeyEventArgsWithText(IKeyboardDevice device, ulong timestamp, IInputRoot root,
RawKeyEventType type, Key key, RawInputModifiers modifiers, string text) : RawKeyEventType type, Key key, RawInputModifiers modifiers, string text) :

4
src/Avalonia.X11/X11Window.Xim.cs

@ -8,9 +8,9 @@ using Avalonia.Threading;
using static Avalonia.X11.XLib; using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
partial class X11Window internal partial class X11Window
{ {
class XimInputMethod : ITextInputMethodImpl, IX11InputMethodControl private class XimInputMethod : ITextInputMethodImpl, IX11InputMethodControl
{ {
private readonly X11Window _parent; private readonly X11Window _parent;
private bool _windowActive, _imeActive; private bool _windowActive, _imeActive;

46
src/Avalonia.X11/X11Window.cs

@ -27,7 +27,7 @@ using static Avalonia.X11.XLib;
// ReSharper disable StringLiteralTypo // ReSharper disable StringLiteralTypo
namespace Avalonia.X11 namespace Avalonia.X11
{ {
unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client internal unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client
{ {
private readonly AvaloniaX11Platform _platform; private readonly AvaloniaX11Platform _platform;
private readonly bool _popup; private readonly bool _popup;
@ -59,7 +59,7 @@ namespace Avalonia.X11
private bool _useRenderWindow = false; private bool _useRenderWindow = false;
private bool _usePositioningFlags = false; private bool _usePositioningFlags = false;
enum XSyncState private enum XSyncState
{ {
None, None,
WaitConfigure, WaitConfigure,
@ -219,7 +219,7 @@ namespace Avalonia.X11
}); });
} }
class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo private class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
{ {
private readonly X11Window _window; private readonly X11Window _window;
private readonly IntPtr _display; private readonly IntPtr _display;
@ -249,7 +249,7 @@ namespace Avalonia.X11
public double Scaling => _window.RenderScaling; public double Scaling => _window.RenderScaling;
} }
void UpdateMotifHints() private void UpdateMotifHints()
{ {
var functions = MotifFunctions.Move | MotifFunctions.Close | MotifFunctions.Resize | var functions = MotifFunctions.Move | MotifFunctions.Close | MotifFunctions.Resize |
MotifFunctions.Minimize | MotifFunctions.Maximize; MotifFunctions.Minimize | MotifFunctions.Maximize;
@ -278,7 +278,7 @@ namespace Avalonia.X11
PropertyMode.Replace, ref hints, 5); PropertyMode.Replace, ref hints, 5);
} }
void UpdateSizeHints(PixelSize? preResize) private void UpdateSizeHints(PixelSize? preResize)
{ {
var min = _minMaxSize.minSize; var min = _minMaxSize.minSize;
var max = _minMaxSize.maxSize; var max = _minMaxSize.maxSize;
@ -386,7 +386,7 @@ namespace Avalonia.X11
public IRenderer CreateRenderer(IRenderRoot root) => public IRenderer CreateRenderer(IRenderRoot root) =>
new CompositingRenderer(root, _platform.Compositor, () => Surfaces); new CompositingRenderer(root, _platform.Compositor, () => Surfaces);
void OnEvent(ref XEvent ev) private void OnEvent(ref XEvent ev)
{ {
if (ev.type == XEventName.MapNotify) if (ev.type == XEventName.MapNotify)
{ {
@ -668,7 +668,7 @@ namespace Avalonia.X11
} }
static RawInputModifiers TranslateModifiers(XModifierMask state) private static RawInputModifiers TranslateModifiers(XModifierMask state)
{ {
var rv = default(RawInputModifiers); var rv = default(RawInputModifiers);
if (state.HasAllFlags(XModifierMask.Button1Mask)) if (state.HasAllFlags(XModifierMask.Button1Mask))
@ -704,13 +704,13 @@ namespace Avalonia.X11
private double _scaling = 1; private double _scaling = 1;
void ScheduleInput(RawInputEventArgs args, ref XEvent xev) private void ScheduleInput(RawInputEventArgs args, ref XEvent xev)
{ {
_x11.LastActivityTimestamp = xev.ButtonEvent.time; _x11.LastActivityTimestamp = xev.ButtonEvent.time;
ScheduleInput(args); ScheduleInput(args);
} }
void DispatchInput(RawInputEventArgs args) private void DispatchInput(RawInputEventArgs args)
{ {
Input?.Invoke(args); Input?.Invoke(args);
if (!args.Handled && args is RawKeyEventArgsWithText text && !string.IsNullOrEmpty(text.Text)) if (!args.Handled && args is RawKeyEventArgsWithText text && !string.IsNullOrEmpty(text.Text))
@ -746,8 +746,8 @@ namespace Avalonia.X11
_rawEventGrouper.HandleEvent(args); _rawEventGrouper.HandleEvent(args);
} }
void MouseEvent(RawPointerEventType type, ref XEvent ev, XModifierMask mods) private void MouseEvent(RawPointerEventType type, ref XEvent ev, XModifierMask mods)
{ {
var mev = new RawPointerEventArgs( var mev = new RawPointerEventArgs(
_mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot, _mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot,
@ -755,7 +755,7 @@ namespace Avalonia.X11
ScheduleInput(mev, ref ev); ScheduleInput(mev, ref ev);
} }
void EnqueuePaint() private void EnqueuePaint()
{ {
if (!_triggeredExpose) if (!_triggeredExpose)
{ {
@ -767,8 +767,8 @@ namespace Avalonia.X11
}, DispatcherPriority.Render); }, DispatcherPriority.Render);
} }
} }
void DoPaint() private void DoPaint()
{ {
Paint?.Invoke(new Rect()); Paint?.Invoke(new Rect());
if (_xSyncCounter != IntPtr.Zero && _xSyncState == XSyncState.WaitPaint) if (_xSyncCounter != IntPtr.Zero && _xSyncState == XSyncState.WaitPaint)
@ -819,8 +819,8 @@ namespace Avalonia.X11
return null; return null;
} }
void Cleanup() private void Cleanup()
{ {
if (_rawEventGrouper != null) if (_rawEventGrouper != null)
{ {
@ -871,7 +871,7 @@ namespace Avalonia.X11
} }
} }
bool ActivateTransientChildIfNeeded() private bool ActivateTransientChildIfNeeded()
{ {
if (_disabled) if (_disabled)
{ {
@ -923,9 +923,9 @@ namespace Avalonia.X11
Resize(size, true, PlatformResizeReason.Layout); Resize(size, true, PlatformResizeReason.Layout);
} }
PixelSize ToPixelSize(Size size) => new PixelSize((int)(size.Width * RenderScaling), (int)(size.Height * RenderScaling)); private PixelSize ToPixelSize(Size size) => new PixelSize((int)(size.Width * RenderScaling), (int)(size.Height * RenderScaling));
void Resize(Size clientSize, bool force, PlatformResizeReason reason) private void Resize(Size clientSize, bool force, PlatformResizeReason reason)
{ {
if (!force && clientSize == ClientSize) if (!force && clientSize == ClientSize)
return; return;
@ -1020,7 +1020,7 @@ namespace Avalonia.X11
.OrderByDescending(x => x.Width + x.Height).FirstOrDefault(); .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
void SendNetWMMessage(IntPtr message_type, IntPtr l0, private void SendNetWMMessage(IntPtr message_type, IntPtr l0,
IntPtr? l1 = null, IntPtr? l2 = null, IntPtr? l3 = null, IntPtr? l4 = null) IntPtr? l1 = null, IntPtr? l2 = null, IntPtr? l3 = null, IntPtr? l4 = null)
{ {
var xev = new XEvent var xev = new XEvent
@ -1044,7 +1044,7 @@ namespace Avalonia.X11
} }
void BeginMoveResize(NetWmMoveResize side, PointerPressedEventArgs e) private void BeginMoveResize(NetWmMoveResize side, PointerPressedEventArgs e)
{ {
var pos = GetCursorPos(_x11); var pos = GetCursorPos(_x11);
XUngrabPointer(_x11.Display, new IntPtr(0)); XUngrabPointer(_x11.Display, new IntPtr(0));
@ -1184,7 +1184,7 @@ namespace Avalonia.X11
ChangeWMAtoms(!value, _x11.Atoms._NET_WM_STATE_SKIP_TASKBAR); ChangeWMAtoms(!value, _x11.Atoms._NET_WM_STATE_SKIP_TASKBAR);
} }
void ChangeWMAtoms(bool enable, params IntPtr[] atoms) private void ChangeWMAtoms(bool enable, params IntPtr[] atoms)
{ {
if (atoms.Length != 1 && atoms.Length != 2) if (atoms.Length != 1 && atoms.Length != 2)
throw new ArgumentException(); throw new ArgumentException();

5
src/Avalonia.X11/XError.cs

@ -2,11 +2,12 @@ using System;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
static class XError internal static class XError
{ {
private static readonly XErrorHandler s_errorHandlerDelegate = Handler; private static readonly XErrorHandler s_errorHandlerDelegate = Handler;
public static XErrorEvent LastError; public static XErrorEvent LastError;
static int Handler(IntPtr display, ref XErrorEvent error)
private static int Handler(IntPtr display, ref XErrorEvent error)
{ {
LastError = error; LastError = error;
return 0; return 0;

16
src/Avalonia.X11/XI2Manager.cs

@ -7,7 +7,7 @@ using static Avalonia.X11.XLib;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
unsafe class XI2Manager internal unsafe class XI2Manager
{ {
private static readonly XiEventType[] DefaultEventTypes = new XiEventType[] private static readonly XiEventType[] DefaultEventTypes = new XiEventType[]
{ {
@ -30,7 +30,7 @@ namespace Avalonia.X11
private bool _multitouch; private bool _multitouch;
private Dictionary<IntPtr, IXI2Client> _clients = new Dictionary<IntPtr, IXI2Client>(); private Dictionary<IntPtr, IXI2Client> _clients = new Dictionary<IntPtr, IXI2Client>();
class DeviceInfo private class DeviceInfo
{ {
public int Id { get; } public int Id { get; }
public XIValuatorClassInfo[] Valuators { get; private set; } public XIValuatorClassInfo[] Valuators { get; private set; }
@ -67,7 +67,7 @@ namespace Avalonia.X11
} }
} }
class PointerDeviceInfo : DeviceInfo private class PointerDeviceInfo : DeviceInfo
{ {
public PointerDeviceInfo(XIDeviceInfo info) : base(info) public PointerDeviceInfo(XIDeviceInfo info) : base(info)
{ {
@ -197,7 +197,7 @@ namespace Avalonia.X11
} }
} }
void OnEnterLeaveEvent(IXI2Client client, ref XIEnterLeaveEvent ev) private void OnEnterLeaveEvent(IXI2Client client, ref XIEnterLeaveEvent ev)
{ {
if (ev.evtype == XiEventType.XI_Leave) if (ev.evtype == XiEventType.XI_Leave)
{ {
@ -215,7 +215,7 @@ namespace Avalonia.X11
} }
} }
void OnDeviceEvent(IXI2Client client, ParsedDeviceEvent ev) private void OnDeviceEvent(IXI2Client client, ParsedDeviceEvent ev)
{ {
if (ev.Type == XiEventType.XI_TouchBegin if (ev.Type == XiEventType.XI_TouchBegin
|| ev.Type == XiEventType.XI_TouchUpdate || ev.Type == XiEventType.XI_TouchUpdate
@ -304,7 +304,7 @@ namespace Avalonia.X11
} }
} }
unsafe class ParsedDeviceEvent internal unsafe class ParsedDeviceEvent
{ {
public XiEventType Type { get; } public XiEventType Type { get; }
public RawInputModifiers Modifiers { get; } public RawInputModifiers Modifiers { get; }
@ -367,8 +367,8 @@ namespace Avalonia.X11
Emulated = ev->flags.HasAllFlags(XiDeviceEventFlags.XIPointerEmulated); Emulated = ev->flags.HasAllFlags(XiDeviceEventFlags.XIPointerEmulated);
} }
} }
interface IXI2Client internal interface IXI2Client
{ {
IInputRoot InputRoot { get; } IInputRoot InputRoot { get; }
void ScheduleXI2Input(RawInputEventArgs args); void ScheduleXI2Input(RawInputEventArgs args);

54
src/Avalonia.X11/XIStructs.cs

@ -10,7 +10,7 @@ using Atom = System.IntPtr;
namespace Avalonia.X11 namespace Avalonia.X11
{ {
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XIAddMasterInfo internal struct XIAddMasterInfo
{ {
public int Type; public int Type;
public IntPtr Name; public IntPtr Name;
@ -19,7 +19,7 @@ namespace Avalonia.X11
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XIRemoveMasterInfo internal struct XIRemoveMasterInfo
{ {
public int Type; public int Type;
public int Deviceid; public int Deviceid;
@ -29,7 +29,7 @@ namespace Avalonia.X11
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XIAttachSlaveInfo internal struct XIAttachSlaveInfo
{ {
public int Type; public int Type;
public int Deviceid; public int Deviceid;
@ -37,14 +37,14 @@ namespace Avalonia.X11
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XIDetachSlaveInfo internal struct XIDetachSlaveInfo
{ {
public int Type; public int Type;
public int Deviceid; public int Deviceid;
}; };
[StructLayout(LayoutKind.Explicit)] [StructLayout(LayoutKind.Explicit)]
struct XIAnyHierarchyChangeInfo internal struct XIAnyHierarchyChangeInfo
{ {
[FieldOffset(0)] [FieldOffset(0)]
public int type; /* must be first element */ public int type; /* must be first element */
@ -59,7 +59,7 @@ namespace Avalonia.X11
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XIModifierState internal struct XIModifierState
{ {
public int Base; public int Base;
public int Latched; public int Latched;
@ -68,14 +68,14 @@ namespace Avalonia.X11
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct XIButtonState internal unsafe struct XIButtonState
{ {
public int MaskLen; public int MaskLen;
public byte* Mask; public byte* Mask;
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct XIValuatorState internal unsafe struct XIValuatorState
{ {
public int MaskLen; public int MaskLen;
public byte* Mask; public byte* Mask;
@ -83,7 +83,7 @@ namespace Avalonia.X11
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct XIEventMask internal unsafe struct XIEventMask
{ {
public int Deviceid; public int Deviceid;
public int MaskLen; public int MaskLen;
@ -91,14 +91,14 @@ namespace Avalonia.X11
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XIAnyClassInfo internal struct XIAnyClassInfo
{ {
public XiDeviceClass Type; public XiDeviceClass Type;
public int Sourceid; public int Sourceid;
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct XIButtonClassInfo internal unsafe struct XIButtonClassInfo
{ {
public int Type; public int Type;
public int Sourceid; public int Sourceid;
@ -108,7 +108,7 @@ namespace Avalonia.X11
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct XIKeyClassInfo internal unsafe struct XIKeyClassInfo
{ {
public int Type; public int Type;
public int Sourceid; public int Sourceid;
@ -117,7 +117,7 @@ namespace Avalonia.X11
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XIValuatorClassInfo internal struct XIValuatorClassInfo
{ {
public int Type; public int Type;
public int Sourceid; public int Sourceid;
@ -132,7 +132,7 @@ namespace Avalonia.X11
/* new in XI 2.1 */ /* new in XI 2.1 */
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XIScrollClassInfo internal struct XIScrollClassInfo
{ {
public int Type; public int Type;
public int Sourceid; public int Sourceid;
@ -142,14 +142,14 @@ namespace Avalonia.X11
public int Flags; public int Flags;
}; };
enum XiScrollType internal enum XiScrollType
{ {
Vertical = 1, Vertical = 1,
Horizontal = 2 Horizontal = 2
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XITouchClassInfo internal struct XITouchClassInfo
{ {
public int Type; public int Type;
public int Sourceid; public int Sourceid;
@ -158,7 +158,7 @@ namespace Avalonia.X11
}; };
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct XIDeviceInfo internal unsafe struct XIDeviceInfo
{ {
public int Deviceid; public int Deviceid;
public IntPtr Name; public IntPtr Name;
@ -169,7 +169,7 @@ namespace Avalonia.X11
public XIAnyClassInfo** Classes; public XIAnyClassInfo** Classes;
} }
enum XiDeviceType internal enum XiDeviceType
{ {
XIMasterPointer = 1, XIMasterPointer = 1,
XIMasterKeyboard = 2, XIMasterKeyboard = 2,
@ -178,13 +178,13 @@ namespace Avalonia.X11
XIFloatingSlave = 5 XIFloatingSlave = 5
} }
enum XiPredefinedDeviceId : int internal enum XiPredefinedDeviceId : int
{ {
XIAllDevices = 0, XIAllDevices = 0,
XIAllMasterDevices = 1 XIAllMasterDevices = 1
} }
enum XiDeviceClass internal enum XiDeviceClass
{ {
XIKeyClass = 0, XIKeyClass = 0,
XIButtonClass = 1, XIButtonClass = 1,
@ -194,7 +194,7 @@ namespace Avalonia.X11
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct XIDeviceChangedEvent internal unsafe struct XIDeviceChangedEvent
{ {
public int Type; /* GenericEvent */ public int Type; /* GenericEvent */
public UIntPtr Serial; /* # of last request processed by server */ public UIntPtr Serial; /* # of last request processed by server */
@ -211,7 +211,7 @@ namespace Avalonia.X11
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
struct XIDeviceEvent internal struct XIDeviceEvent
{ {
public XEventName type; /* GenericEvent */ public XEventName type; /* GenericEvent */
public UIntPtr serial; /* # of last request processed by server */ public UIntPtr serial; /* # of last request processed by server */
@ -238,7 +238,7 @@ namespace Avalonia.X11
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct XIEnterLeaveEvent internal unsafe struct XIEnterLeaveEvent
{ {
public XEventName type; /* GenericEvent */ public XEventName type; /* GenericEvent */
public UIntPtr serial; /* # of last request processed by server */ public UIntPtr serial; /* # of last request processed by server */
@ -266,14 +266,14 @@ namespace Avalonia.X11
} }
[Flags] [Flags]
public enum XiDeviceEventFlags : int internal enum XiDeviceEventFlags : int
{ {
None = 0, None = 0,
XIPointerEmulated = (1 << 16) XIPointerEmulated = (1 << 16)
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
unsafe struct XIEvent internal unsafe struct XIEvent
{ {
public int type; /* GenericEvent */ public int type; /* GenericEvent */
public UIntPtr serial; /* # of last request processed by server */ public UIntPtr serial; /* # of last request processed by server */
@ -284,7 +284,7 @@ namespace Avalonia.X11
public IntPtr time; public IntPtr time;
} }
enum XiEventType internal enum XiEventType
{ {
XI_DeviceChanged = 1, XI_DeviceChanged = 1,
XI_KeyPress = 2, XI_KeyPress = 2,
@ -315,7 +315,7 @@ namespace Avalonia.X11
XI_LASTEVENT = XI_BarrierLeave, XI_LASTEVENT = XI_BarrierLeave,
} }
enum XiEnterLeaveDetail internal enum XiEnterLeaveDetail
{ {
XINotifyAncestor = 0, XINotifyAncestor = 0,
XINotifyVirtual = 1, XINotifyVirtual = 1,

10
src/Avalonia.X11/XLib.cs

@ -16,11 +16,11 @@ namespace Avalonia.X11
{ {
internal unsafe static class XLib internal unsafe static class XLib
{ {
const string libX11 = "libX11.so.6"; private const string libX11 = "libX11.so.6";
const string libX11Randr = "libXrandr.so.2"; private const string libX11Randr = "libXrandr.so.2";
const string libX11Ext = "libXext.so.6"; private const string libX11Ext = "libXext.so.6";
const string libXInput = "libXi.so.6"; private const string libXInput = "libXi.so.6";
const string libXCursor = "libXcursor.so.1"; private const string libXCursor = "libXcursor.so.1";
[DllImport(libX11)] [DllImport(libX11)]
public static extern IntPtr XOpenDisplay(IntPtr display); public static extern IntPtr XOpenDisplay(IntPtr display);

27
src/Browser/Avalonia.Browser/AvaloniaView.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices.JavaScript; using System.Runtime.InteropServices.JavaScript;
using Avalonia.Browser.Interop; using Avalonia.Browser.Interop;
@ -15,6 +16,7 @@ using Avalonia.Platform;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using Avalonia.Threading; using Avalonia.Threading;
using SkiaSharp; using SkiaSharp;
using static System.Runtime.CompilerServices.RuntimeHelpers;
namespace Avalonia.Browser namespace Avalonia.Browser
{ {
@ -94,6 +96,7 @@ namespace Avalonia.Browser
InputHelper.SubscribeTextEvents( InputHelper.SubscribeTextEvents(
_inputElement, _inputElement,
OnBeforeInput,
OnTextInput, OnTextInput,
OnCompositionStart, OnCompositionStart,
OnCompositionUpdate, OnCompositionUpdate,
@ -316,6 +319,30 @@ namespace Avalonia.Browser
return _topLevelImpl.RawTextEvent(data); return _topLevelImpl.RawTextEvent(data);
} }
private bool OnBeforeInput(JSObject arg, int start, int end)
{
var type = arg.GetPropertyAsString("inputType");
if (type != "deleteByComposition")
{
if (type == "deleteContentBackward")
{
start = _inputElement.GetPropertyAsInt32("selectionStart");
end = _inputElement.GetPropertyAsInt32("selectionEnd");
}
else
{
start = -1;
end = -1;
}
}
if(start != -1 && end != -1 && _client != null)
{
_client.SelectInSurroundingText(start, end);
}
return false;
}
private bool OnCompositionStart (JSObject args) private bool OnCompositionStart (JSObject args)
{ {
if (_client == null) if (_client == null)

2
src/Browser/Avalonia.Browser/Interop/InputHelper.cs

@ -18,6 +18,8 @@ internal static partial class InputHelper
[JSImport("InputHelper.subscribeTextEvents", AvaloniaModule.MainModuleName)] [JSImport("InputHelper.subscribeTextEvents", AvaloniaModule.MainModuleName)]
public static partial void SubscribeTextEvents( public static partial void SubscribeTextEvents(
JSObject htmlElement, JSObject htmlElement,
[JSMarshalAs<JSType.Function<JSType.Object, JSType.Number, JSType.Number, JSType.Boolean>>]
Func<JSObject, int, int, bool> onBeforeInput,
[JSMarshalAs<JSType.Function<JSType.String, JSType.String, JSType.Boolean>>] [JSMarshalAs<JSType.Function<JSType.String, JSType.String, JSType.Boolean>>]
Func<string, string?, bool> onInput, Func<string, string?, bool> onInput,
[JSMarshalAs<JSType.Function<JSType.Object, JSType.Boolean>>] [JSMarshalAs<JSType.Function<JSType.Object, JSType.Boolean>>]

2
src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs

@ -6,7 +6,7 @@ using Avalonia.Reactive;
namespace Avalonia.Browser.Skia namespace Avalonia.Browser.Skia
{ {
public class BrowserSkiaGpu : ISkiaGpu internal class BrowserSkiaGpu : ISkiaGpu
{ {
public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces) public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces)
{ {

20
src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts

@ -47,6 +47,7 @@ export class InputHelper {
public static subscribeTextEvents( public static subscribeTextEvents(
element: HTMLInputElement, element: HTMLInputElement,
beforeInputCallback: (args: InputEvent, start: number, end: number) => boolean,
inputCallback: (type: string, data: string | null) => boolean, inputCallback: (type: string, data: string | null) => boolean,
compositionStartCallback: (args: CompositionEvent) => boolean, compositionStartCallback: (args: CompositionEvent) => boolean,
compositionUpdateCallback: (args: CompositionEvent) => boolean, compositionUpdateCallback: (args: CompositionEvent) => boolean,
@ -68,6 +69,25 @@ export class InputHelper {
}; };
element.addEventListener("compositionstart", compositionStartHandler); element.addEventListener("compositionstart", compositionStartHandler);
const beforeInputHandler = (args: InputEvent) => {
const ranges = args.getTargetRanges();
let start = -1;
let end = -1;
if (ranges.length > 0) {
start = ranges[0].startOffset;
end = ranges[0].endOffset;
}
if (args.inputType === "insertCompositionText") {
start = 2;
end = start + 2;
}
if (beforeInputCallback(args, start, end)) {
args.preventDefault();
}
};
element.addEventListener("beforeinput", beforeInputHandler);
const compositionUpdateHandler = (args: CompositionEvent) => { const compositionUpdateHandler = (args: CompositionEvent) => {
if (compositionUpdateCallback(args)) { if (compositionUpdateCallback(args)) {
args.preventDefault(); args.preventDefault();

4
src/Shared/RawEventGrouping.cs

@ -17,8 +17,8 @@ internal class RawEventGrouper : IDisposable
private readonly Action<RawInputEventArgs> _eventCallback; private readonly Action<RawInputEventArgs> _eventCallback;
private readonly Queue<RawInputEventArgs> _inputQueue = new(); private readonly Queue<RawInputEventArgs> _inputQueue = new();
private readonly Action _dispatchFromQueue; private readonly Action _dispatchFromQueue;
readonly Dictionary<long, RawPointerEventArgs> _lastTouchPoints = new(); private readonly Dictionary<long, RawPointerEventArgs> _lastTouchPoints = new();
RawInputEventArgs? _lastEvent; private RawInputEventArgs? _lastEvent;
public RawEventGrouper(Action<RawInputEventArgs> eventCallback) public RawEventGrouper(Action<RawInputEventArgs> eventCallback)
{ {

5
src/Skia/Avalonia.Skia/GlyphRunImpl.cs

@ -1,15 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
using SkiaSharp; using SkiaSharp;
#nullable enable #nullable enable
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
/// <inheritdoc /> internal class GlyphRunImpl : IGlyphRunImpl
[Unstable]
public class GlyphRunImpl : IGlyphRunImpl
{ {
public GlyphRunImpl(SKTextBlob textBlob, Size size, Point baselineOrigin) public GlyphRunImpl(SKTextBlob textBlob, Size size, Point baselineOrigin)
{ {

4
src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs

@ -1,14 +1,12 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Metadata;
using HarfBuzzSharp; using HarfBuzzSharp;
using SkiaSharp; using SkiaSharp;
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
[Unstable] internal class GlyphTypefaceImpl : IGlyphTypeface
public class GlyphTypefaceImpl : IGlyphTypeface
{ {
private bool _isDisposed; private bool _isDisposed;

2
src/Skia/Avalonia.Skia/SKTypefaceCollection.cs

@ -4,7 +4,7 @@ using SkiaSharp;
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
public class SKTypefaceCollection internal class SKTypefaceCollection
{ {
private readonly ConcurrentDictionary<Typeface, SKTypeface> _typefaces = private readonly ConcurrentDictionary<Typeface, SKTypeface> _typefaces =
new ConcurrentDictionary<Typeface, SKTypeface>(); new ConcurrentDictionary<Typeface, SKTypeface>();

2
src/Skia/Avalonia.Skia/SKTypefaceCollectionCache.cs

@ -7,7 +7,7 @@ using SkiaSharp;
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
public static class SKTypefaceCollectionCache internal static class SKTypefaceCollectionCache
{ {
private static readonly ConcurrentDictionary<FontFamily, SKTypefaceCollection> s_cachedCollections; private static readonly ConcurrentDictionary<FontFamily, SKTypefaceCollection> s_cachedCollections;

22
src/Skia/Avalonia.Skia/readme.md

@ -1,22 +0,0 @@
DrawingContextImpl
- Alpha support missing as SkiaSharp does not expose this
- Gradient Shader caching?
- Pen Dash styles
Formatted Text Rendering
- Minor polish
Linux
- Need gpu platform implementation
macOS
- Need gpu platform implementation
Android
- Not tested at all yet
iOS
- Not tested at all yet
General
- Get Skia Unit Tests passing (most of the issues are related to antialiasing)

6
src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj

@ -5,11 +5,6 @@
<PackageId>Avalonia.Direct2D1</PackageId> <PackageId>Avalonia.Direct2D1</PackageId>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Include="..\Avalonia.Win32\Interop\UnmanagedMethods.cs">
<Link>UnmanagedMethods.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Avalonia.Win32\Avalonia.Win32.csproj" /> <ProjectReference Include="..\Avalonia.Win32\Avalonia.Win32.csproj" />
@ -22,6 +17,7 @@
<ItemGroup Label="InternalsVisibleTo"> <ItemGroup Label="InternalsVisibleTo">
<InternalsVisibleTo Include="Avalonia.Direct2D1.RenderTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Direct2D1.RenderTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Direct2D1.UnitTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Direct2D1.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Win32.Interop, PublicKey=$(AvaloniaPublicKey)"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

2
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -27,7 +27,7 @@ namespace Avalonia
namespace Avalonia.Direct2D1 namespace Avalonia.Direct2D1
{ {
public class Direct2D1Platform : IPlatformRenderInterface internal class Direct2D1Platform : IPlatformRenderInterface
{ {
private static readonly Direct2D1Platform s_instance = new Direct2D1Platform(); private static readonly Direct2D1Platform s_instance = new Direct2D1Platform();

33
src/Windows/Avalonia.Direct2D1/Disposable.cs

@ -1,33 +0,0 @@
using System;
namespace Avalonia.Direct2D1
{
public class Disposable<T> : IDisposable where T : IDisposable
{
private readonly IDisposable _extra;
public Disposable(T inner)
{
Inner = inner;
}
public Disposable(T inner, IDisposable extra)
{
Inner = inner;
_extra = extra;
}
public T Inner { get; }
public static implicit operator T(Disposable<T> i)
{
return i.Inner;
}
public void Dispose()
{
Inner.Dispose();
_extra?.Dispose();
}
}
}

2
src/Windows/Avalonia.Direct2D1/ILayerFactory.cs

@ -2,7 +2,7 @@
namespace Avalonia.Direct2D1 namespace Avalonia.Direct2D1
{ {
public interface ILayerFactory internal interface ILayerFactory
{ {
IDrawingContextLayerImpl CreateLayer(Size size); IDrawingContextLayerImpl CreateLayer(Size size);
} }

4
src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs

@ -1,10 +1,8 @@
using System; using System;
using Avalonia.Metadata;
namespace Avalonia.Direct2D1.Media namespace Avalonia.Direct2D1.Media
{ {
[Unstable] internal abstract class BrushImpl : IDisposable
public abstract class BrushImpl : IDisposable
{ {
public SharpDX.Direct2D1.Brush PlatformBrush { get; set; } public SharpDX.Direct2D1.Brush PlatformBrush { get; set; }

4
src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileEnumerator.cs

@ -6,7 +6,7 @@ namespace Avalonia.Direct2D1.Media
/// <summary> /// <summary>
/// Resource FontFileEnumerator. /// Resource FontFileEnumerator.
/// </summary> /// </summary>
public class DWriteResourceFontFileEnumerator : CallbackBase, FontFileEnumerator internal class DWriteResourceFontFileEnumerator : CallbackBase, FontFileEnumerator
{ {
private readonly Factory _factory; private readonly Factory _factory;
private readonly FontFileLoader _loader; private readonly FontFileLoader _loader;
@ -64,4 +64,4 @@ namespace Avalonia.Direct2D1.Media
} }
} }
} }
} }

2
src/Windows/Avalonia.Direct2D1/Media/DWriteResourceFontFileStream.cs

@ -7,7 +7,7 @@ namespace Avalonia.Direct2D1.Media
/// <summary> /// <summary>
/// This FontFileStream implementation is reading data from a <see cref="DataStream"/>. /// This FontFileStream implementation is reading data from a <see cref="DataStream"/>.
/// </summary> /// </summary>
public class DWriteResourceFontFileStream : CallbackBase, FontFileStream internal class DWriteResourceFontFileStream : CallbackBase, FontFileStream
{ {
private readonly DataStream _stream; private readonly DataStream _stream;

4
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -11,15 +11,13 @@ using SharpDX;
using SharpDX.Direct2D1; using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop; using SharpDX.Mathematics.Interop;
using BitmapInterpolationMode = Avalonia.Media.Imaging.BitmapInterpolationMode; using BitmapInterpolationMode = Avalonia.Media.Imaging.BitmapInterpolationMode;
using Avalonia.Metadata;
namespace Avalonia.Direct2D1.Media namespace Avalonia.Direct2D1.Media
{ {
/// <summary> /// <summary>
/// Draws using Direct2D1. /// Draws using Direct2D1.
/// </summary> /// </summary>
[Unstable] internal class DrawingContextImpl : IDrawingContextImpl
public class DrawingContextImpl : IDrawingContextImpl
{ {
private readonly IVisualBrushRenderer _visualBrushRenderer; private readonly IVisualBrushRenderer _visualBrushRenderer;
private readonly ILayerFactory _layerFactory; private readonly ILayerFactory _layerFactory;

4
src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs

@ -1,5 +1,4 @@
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
using SharpDX.Direct2D1; using SharpDX.Direct2D1;
@ -8,8 +7,7 @@ namespace Avalonia.Direct2D1.Media
/// <summary> /// <summary>
/// The platform-specific interface for <see cref="Avalonia.Media.Geometry"/>. /// The platform-specific interface for <see cref="Avalonia.Media.Geometry"/>.
/// </summary> /// </summary>
[Unstable] internal abstract class GeometryImpl : IGeometryImpl
public abstract class GeometryImpl : IGeometryImpl
{ {
private const float ContourApproximation = 0.0001f; private const float ContourApproximation = 0.0001f;

4
src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs

@ -1,6 +1,5 @@
using System; using System;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Metadata;
using HarfBuzzSharp; using HarfBuzzSharp;
using SharpDX.DirectWrite; using SharpDX.DirectWrite;
using FontMetrics = Avalonia.Media.FontMetrics; using FontMetrics = Avalonia.Media.FontMetrics;
@ -9,8 +8,7 @@ using GlyphMetrics = Avalonia.Media.GlyphMetrics;
namespace Avalonia.Direct2D1.Media namespace Avalonia.Direct2D1.Media
{ {
[Unstable] internal class GlyphTypefaceImpl : IGlyphTypeface
public class GlyphTypefaceImpl : IGlyphTypeface
{ {
private bool _isDisposed; private bool _isDisposed;

4
src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs

@ -1,13 +1,11 @@
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Rendering.Utilities; using Avalonia.Rendering.Utilities;
using Avalonia.Utilities; using Avalonia.Utilities;
using SharpDX.Direct2D1; using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media namespace Avalonia.Direct2D1.Media
{ {
[Unstable] internal sealed class ImageBrushImpl : BrushImpl
public sealed class ImageBrushImpl : BrushImpl
{ {
private readonly OptionalDispose<Bitmap> _bitmap; private readonly OptionalDispose<Bitmap> _bitmap;

4
src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs

@ -1,13 +1,11 @@
using System; using System;
using System.IO; using System.IO;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
using D2DBitmap = SharpDX.Direct2D1.Bitmap; using D2DBitmap = SharpDX.Direct2D1.Bitmap;
namespace Avalonia.Direct2D1.Media namespace Avalonia.Direct2D1.Media
{ {
[Unstable] internal abstract class BitmapImpl : IBitmapImpl, IDisposable
public abstract class BitmapImpl : IBitmapImpl, IDisposable
{ {
public abstract Vector Dpi { get; } public abstract Vector Dpi { get; }
public abstract PixelSize PixelSize { get; } public abstract PixelSize PixelSize { get; }

4
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs

@ -1,6 +1,5 @@
using System; using System;
using System.IO; using System.IO;
using Avalonia.Metadata;
using SharpDX.WIC; using SharpDX.WIC;
using Bitmap = SharpDX.Direct2D1.Bitmap; using Bitmap = SharpDX.Direct2D1.Bitmap;
@ -9,8 +8,7 @@ namespace Avalonia.Direct2D1.Media
/// <summary> /// <summary>
/// A Direct2D Bitmap implementation that uses a GPU memory bitmap as its image. /// A Direct2D Bitmap implementation that uses a GPU memory bitmap as its image.
/// </summary> /// </summary>
[Unstable] internal class D2DBitmapImpl : BitmapImpl
public class D2DBitmapImpl : BitmapImpl
{ {
private readonly Bitmap _direct2DBitmap; private readonly Bitmap _direct2DBitmap;

4
src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs

@ -1,6 +1,5 @@
using System; using System;
using System.IO; using System.IO;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -10,8 +9,7 @@ using D2DBitmap = SharpDX.Direct2D1.Bitmap;
namespace Avalonia.Direct2D1.Media.Imaging namespace Avalonia.Direct2D1.Media.Imaging
{ {
[Unstable] internal class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IDrawingContextLayerImpl, ILayerFactory
public class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IDrawingContextLayerImpl, ILayerFactory
{ {
private readonly BitmapRenderTarget _renderTarget; private readonly BitmapRenderTarget _renderTarget;

4
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs

@ -6,7 +6,6 @@ using SharpDX.WIC;
using APixelFormat = Avalonia.Platform.PixelFormat; using APixelFormat = Avalonia.Platform.PixelFormat;
using AlphaFormat = Avalonia.Platform.AlphaFormat; using AlphaFormat = Avalonia.Platform.AlphaFormat;
using D2DBitmap = SharpDX.Direct2D1.Bitmap; using D2DBitmap = SharpDX.Direct2D1.Bitmap;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
using PixelFormat = SharpDX.WIC.PixelFormat; using PixelFormat = SharpDX.WIC.PixelFormat;
@ -15,8 +14,7 @@ namespace Avalonia.Direct2D1.Media
/// <summary> /// <summary>
/// A WIC implementation of a <see cref="Avalonia.Media.Imaging.Bitmap"/>. /// A WIC implementation of a <see cref="Avalonia.Media.Imaging.Bitmap"/>.
/// </summary> /// </summary>
[Unstable] internal class WicBitmapImpl : BitmapImpl, IReadableBitmapImpl
public class WicBitmapImpl : BitmapImpl, IReadableBitmapImpl
{ {
private readonly BitmapDecoder _decoder; private readonly BitmapDecoder _decoder;

4
src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs

@ -1,13 +1,11 @@
using System; using System;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using SharpDX.Direct2D1; using SharpDX.Direct2D1;
namespace Avalonia.Direct2D1.Media namespace Avalonia.Direct2D1.Media
{ {
[Unstable] internal class WicRenderTargetBitmapImpl : WicBitmapImpl, IDrawingContextLayerImpl
public class WicRenderTargetBitmapImpl : WicBitmapImpl, IDrawingContextLayerImpl
{ {
private readonly WicRenderTarget _renderTarget; private readonly WicRenderTarget _renderTarget;

2
src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs

@ -6,7 +6,7 @@ using PixelFormat = Avalonia.Platform.PixelFormat;
namespace Avalonia.Direct2D1.Media.Imaging namespace Avalonia.Direct2D1.Media.Imaging
{ {
class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl internal class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl
{ {
public WriteableWicBitmapImpl(Stream stream, int decodeSize, bool horizontal, public WriteableWicBitmapImpl(Stream stream, int decodeSize, bool horizontal,
Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode) Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode)

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save