Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Svg.Editor.Avalonia.Forms/Dialog/IUserInteraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

namespace Svg.Editor.Avalon.Forms.Dialog;

public record InputWithOptionsResponse(bool Ok, string? Text, string? SelectedOption, int SelectedIndex);

public interface IUserInteraction
{
Task<bool> ConfirmAsync(string message, string? title = null, string okButton = "OK", string cancelButton = "Cancel", bool cancellable = false);
Expand Down Expand Up @@ -41,6 +43,17 @@ Task<ConfirmThreeButtonsResponse> ConfirmThreeButtonsAsync(

Task<TimeSpan?> PromptTimeAsync(TimeSpan? selectedTime);
*/

Task<InputWithOptionsResponse> InputWithOptionsAsync(
string message,
IEnumerable<string> options,
string? title = null,
string okButton = "OK",
string cancelButton = "Cancel",
string? initialText = null,
string? placeholder = null,
int selectedIndex = 0,
bool cancellable = true);

void Dismiss();
}
75 changes: 66 additions & 9 deletions Svg.Editor.Avalonia.Forms/Dialog/UserInteractionService.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Labs.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using Svg.Editor.Avalon.Forms.Dialog.Views;
using Svg.Editor.Avalon.Forms.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Svg.Editor.Avalon.Forms.Dialog;

public class UserInteractionService : IUserInteraction
{
private readonly IColorPickerState _colorPickerState;

public UserInteractionService()
{
_colorPickerState = SvgEngine.Resolve<IColorPickerState>();
}

public async Task<bool> ConfirmAsync(string message, string? title = null, string okButton = "OK", string cancelButton = "Cancel", bool cancellable = false)
{
return await Dispatcher.UIThread.InvokeAsync(async () =>
Expand Down Expand Up @@ -161,17 +170,23 @@ public Task<Color> ColorPickerAsync(string? title = null, string okButton = "OK"
PrimaryButtonText = okButton,
CloseButtonText = cancellable ? cancelButton : null,
};
var vm = new ColorPickerDialogViewModel();
var vm = new ColorPickerDialogViewModel()
{
SelectedColor = _colorPickerState.LastPickedColor,
Comment thread
gentledepp marked this conversation as resolved.
};

vm.Initialize(d);

d.Content = new ColorPickerDialog() { DataContext = vm };

var r = await d.ShowAsync();

if (r == ContentDialogResult.Primary)
return vm.GetResult();
return Colors.Black;
if (r == ContentDialogResult.Primary){
var result = vm.GetResult();
_colorPickerState.LastPickedColor = result;
return result;
}
return _colorPickerState.LastPickedColor;
});
}

Expand Down Expand Up @@ -223,6 +238,48 @@ public void Dismiss()
});
}

public Task<InputWithOptionsResponse> InputWithOptionsAsync(string message, IEnumerable<string> options, string? title = null, string okButton = "OK", string cancelButton = "Cancel", string? initialText = null, string? placeholder = null, int selectedIndex = 0, bool cancellable = true)
{
return Dispatcher.UIThread.InvokeAsync(async () =>
{
var optionsList = options?.ToList() ?? new List<string>();

var d = new ContentDialog()
{
Title = title,
Content = message,
IsSecondaryButtonEnabled = false,
PrimaryButtonText = okButton,
CloseButtonText = cancellable ? cancelButton : null,
};

var vm = new TextOptionsDialogResultViewModel
{
UserInput = initialText,
WatermarkText = placeholder,
Options = optionsList,
SelectedIndex = optionsList.Count > 0
? Math.Clamp(selectedIndex, 0, optionsList.Count - 1)
: -1,
CanCancel = cancellable
};
vm.Initialize(d);

d.Content = new TextOptionsDialogContent { DataContext = vm };
d.AttachKeyboardControl(cancellable);

var r = await d.ShowAsync();

if (r == ContentDialogResult.Primary)
{
var result = vm.GetResult();
return new InputWithOptionsResponse(true, result?.Text, result?.SelectedOption, result?.SelectedIndex ?? -1);
}

return new InputWithOptionsResponse(false, null, null, -1);
});
}


#endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected override bool CanClose(ContentDialogResult result)
return base.CanClose(result);
}

internal override object? GetResult()
public override object? GetResult()
{
return SelectedItem?.Value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,15 @@ public class ColorPickerDialogViewModel : ContentDialogResultViewModelBase<Color
private Color _color;
public Color SelectedColor
{
get
{
return _color;
}
set
{
_color = value;
}
get => _color;
set => _color = value;
}

public ColorPickerDialogViewModel()
{
}

internal override Color GetResult()
public override Color GetResult()
{
return SelectedColor;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ namespace Svg.Editor.Avalon.Forms.Dialog.Views;

public abstract class ContentDialogResultViewModelBase<TResult> : ContentDialogViewModelBase
{
internal abstract TResult GetResult();
public abstract TResult GetResult();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public string? WatermarkText
}


internal override string? GetResult()
public override string? GetResult()
{
return UserInput;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="using:Svg.Editor.Avalon.Forms.Dialog.Views"
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="250"
x:Class="Svg.Editor.Avalon.Forms.TextOptionsDialogContent"
x:DataType="views:TextOptionsDialogResultViewModel">
<StackPanel Orientation="Vertical" Spacing="8">
<ContentControl Content="{Binding Content}" IsVisible="{Binding !!Content}" />
<TextBox Text="{Binding UserInput}"
AcceptsReturn="True"
Watermark="{Binding WatermarkText}"
AttachedToVisualTree="InputField_OnAttachedToVisualTree" />
<ComboBox ItemsSource="{Binding Options}"
SelectedIndex="{Binding SelectedIndex}"
IsVisible="{Binding Options.Count}"
Comment thread
Zeljko-Predjeskovic marked this conversation as resolved.
HorizontalAlignment="Stretch" />
</StackPanel></UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;

namespace Svg.Editor.Avalon.Forms;

public partial class TextOptionsDialogContent : UserControl
{
public TextOptionsDialogContent()
{
InitializeComponent();
}

private void InputField_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{
if (sender is InputElement inputElement)
{
Dispatcher.UIThread.Post(() =>
{
inputElement.Focus(NavigationMethod.Unspecified, KeyModifiers.None);
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Svg.Editor.Avalon.Forms.Dialog.Views;

public record TextOptionsResult(string? Text, string? SelectedOption, int SelectedIndex);

public class TextOptionsDialogResultViewModel : ContentDialogResultViewModelBase<TextOptionsResult>
{
private string? _userInput;
public string? UserInput
{
get => _userInput;
set => SetField(ref _userInput, value);
}

private string? _watermarkText;
public string? WatermarkText
{
get => _watermarkText;
set => SetField(ref _watermarkText, value);
}

private IReadOnlyList<string> _options = Array.Empty<string>();
public IReadOnlyList<string> Options
{
get => _options;
set => SetField(ref _options, value);
}

private int _selectedIndex;
public int SelectedIndex
{
get => _selectedIndex;
set => SetField(ref _selectedIndex, value);
}

public override TextOptionsResult? GetResult()
{
var selected = SelectedIndex >= 0 && SelectedIndex < Options.Count
? Options[SelectedIndex]
: null;

return new TextOptionsResult(UserInput, selected, SelectedIndex);
}
}
18 changes: 18 additions & 0 deletions Svg.Editor.Avalonia.Forms/Services/IColorPickerState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Media;

namespace Svg.Editor.Avalon.Forms.Services
{
public interface IColorPickerState
{
Color LastPickedColor { get; set; }
}

public class ColorPickerState : IColorPickerState
{
public Color LastPickedColor { get; set; } = Colors.Black;
}

}
45 changes: 22 additions & 23 deletions Svg.Editor.Avalonia.Forms/Services/TextInputService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,34 @@ public TextInputService()
public async Task<TextTool.TextProperties> GetUserInput(string title, string textValue = "",
IEnumerable<string> textSizeOptions = null, int textSizeSelected = 0, int maxTextLength = -1)
{
var result = await _userInteractionService.InputAsync("Text edit", title, "Ok", "Cancel", textValue, placeholder: "Enter text");
if (maxTextLength != -1)
{
while (result.Text.Length > 2)
{
result = await _userInteractionService.InputAsync("Text edit", title, "Ok", "Cancel", textValue, placeholder: "Enter text");
}
}
var defaultResult = new TextTool.TextProperties
{
FontSizeIndex = textSizeSelected,
LineHeight = 12f,
Text = textValue
};

var text = result.Text;
var sizeOptions = textSizeOptions?.ToList() ?? new List<string>();

var result = await _userInteractionService.InputWithOptionsAsync(
"Text edit",
sizeOptions,
title,
"Ok",
"Cancel",
textValue,
placeholder: "Enter text",
selectedIndex: textSizeSelected);

if (!result.Ok)
{
return defaultResult;
return new TextTool.TextProperties
{
FontSizeIndex = textSizeSelected,
LineHeight = 12f,
Text = textValue
};
}

int sizeIndex = textSizeSelected;
if (textSizeOptions != null)
{
var sizeResult = await _userInteractionService.ActionSheetAsync("Font size", textSizeOptions.ToArray());
var text = result.Text ?? string.Empty;
if (maxTextLength != -1 && text.Length > maxTextLength)
text = text.Substring(0, maxTextLength);

sizeIndex = textSizeOptions.ToList().IndexOf(sizeResult);
sizeIndex = sizeIndex >= 0 ? sizeIndex : textSizeSelected;
}
var sizeIndex = result.SelectedIndex >= 0 ? result.SelectedIndex : textSizeSelected;

return new TextTool.TextProperties
{
Expand Down
4 changes: 3 additions & 1 deletion Svg.Editor.Avalonia.Forms/Svg.Editor.Avalon.Forms.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<Version>3.2.0-optiq04</Version>
<Version>3.2.0-optiq05</Version>
<PackageReleaseNotes>
#3.2.0-optiq05
Improved TextInputService and Colorpicker
#3.2.0-optiq04
Improved marker options input service and StrokeStyleOptionsInputService
#3.2.0-optiq01
Expand Down
1 change: 1 addition & 0 deletions Svg.Editor.Avalonia.Forms/SvgEditorForms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public static void Init(Avalonia.Application app)
SvgEngine.Register<IPickImageService>(() => new FormsPickImageService());
SvgEngine.Register<IPinInputService>(() => new PinInputService());
SvgEngine.Register<IToolTipInfoService>(() => new ToolTipInfoService());
SvgEngine.RegisterSingleton<IColorPickerState>(() => new ColorPickerState());
SvgEngine.Register<IUserInteraction>(() => new UserInteractionService());

app.DataTemplates.Add(new MenuItemHeaderTemplate());
Expand Down
4 changes: 3 additions & 1 deletion Svg.Editor.Avalonia.Views/Svg.Editor.Avalon.Views.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<Version>3.2.0-optiq04</Version>
<Version>3.2.0-optiq05</Version>
<PackageReleaseNotes>
#3.2.0-optiq05
Improved TextInputService and Colorpicker
#3.2.0-optiq04
Improved marker options input service and StrokeStyleOptionsInputService
#3.2.0-optiq01
Expand Down
Loading
Loading