-
-
Notifications
You must be signed in to change notification settings - Fork 51
Add expression support across properties #1431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
1caad3c
feat: implement expression support in animatable properties and relat…
yuto-trd bbd112b
feat: add expression support to property adapters and related classes
yuto-trd 86c7054
feat: add Microsoft.CodeAnalysis.CSharp.Scripting package reference
yuto-trd b10de0f
feat: add expression editing functionality to property editor
yuto-trd d52c5cd
feat: replace Observable.Return with Observable.ReturnThenNever in va…
yuto-trd d3849fe
feat: replace dialog with ExpressionEditorFlyout for editing expressi…
yuto-trd 0798e5f
feat: update property editor to use Observable.ReturnThenNever for be…
yuto-trd 2b2d1dc
feat: add expression status indication in property editor menu
yuto-trd 9756625
fix: hash set usage in CoreObjectExtensions
yuto-trd 6842756
feat: add copy property path and get property code functionality in p…
yuto-trd 5b2eb83
feat: add circular reference detection during expression evaluation
yuto-trd 4ba2ad8
feat: update GetValue method to use RenderContext instead of TimeSpan
yuto-trd 7f795dc
feat: enhance expression evaluation with context management and circu…
yuto-trd a7c87be
feat: add expression handling in EngineObject for serialization and d…
yuto-trd 3261bdd
Update src/Beutl.ProjectSystem/NodeTree/NodePropertyAdapter.cs
yuto-trd cb0faf6
refactor: PropertyEditorMenu.axaml.cs
yuto-trd 9b893ed
feat: refactor Expression class to use lazy parsing and improve error…
yuto-trd 1c85656
refactor: ValueEditorViewModel.cs
yuto-trd 0558869
test: add unit tests for Expression, ExpressionContext, and PropertyL…
yuto-trd d193b89
Initial plan
Copilot f529b98
Add error display when expression validation fails
Copilot d40ca44
Use SystemFillColorCriticalBrush and refactor SetExpression to return…
Copilot d242806
Fix formatting - normalize blank line before return statement
Copilot 95ffbc1
Remove Validator, perform validation in Confirmed event handler
Copilot 310d0f0
refactor: streamline expression confirmation handling and improve tex…
yuto-trd e602fee
Merge pull request #1432 from b-editor/copilot/sub-pr-1431
yuto-trd deaf005
refactor: Prevent AnimatableProperty.GetValue from swallowing exceptions
yuto-trd 046be01
feat: implement ExpressionEditorFlyoutPresenter for improved expressi…
yuto-trd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
src/Beutl.Controls/PropertyEditors/ExpressionEditorFlyoutPresenter.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| #nullable enable | ||
|
|
||
| using System.Reactive.Disposables; | ||
|
|
||
| using Avalonia; | ||
| using Avalonia.Controls; | ||
| using Avalonia.Controls.Primitives; | ||
| using Avalonia.Interactivity; | ||
|
|
||
| using Beutl.Reactive; | ||
|
|
||
| namespace Beutl.Controls.PropertyEditors; | ||
|
|
||
| public class ExpressionEditorFlyoutPresenter : DraggablePickerFlyoutPresenter | ||
| { | ||
| public static readonly StyledProperty<string?> ExpressionTextProperty = | ||
| AvaloniaProperty.Register<ExpressionEditorFlyoutPresenter, string?>(nameof(ExpressionText)); | ||
|
|
||
| public static readonly StyledProperty<string?> ErrorMessageProperty = | ||
| AvaloniaProperty.Register<ExpressionEditorFlyoutPresenter, string?>(nameof(ErrorMessage)); | ||
|
|
||
| private readonly CompositeDisposable _disposables = []; | ||
| private const string HasErrorPseudoClass = ":has-error"; | ||
| private const string HelpTabPseudoClass = ":help-tab"; | ||
|
|
||
| private RadioButton? _inputTabButton; | ||
| private RadioButton? _helpTabButton; | ||
|
|
||
| public string? ExpressionText | ||
| { | ||
| get => GetValue(ExpressionTextProperty); | ||
| set => SetValue(ExpressionTextProperty, value); | ||
| } | ||
|
|
||
| public string? ErrorMessage | ||
| { | ||
| get => GetValue(ErrorMessageProperty); | ||
| set => SetValue(ErrorMessageProperty, value); | ||
| } | ||
|
|
||
| protected override void OnApplyTemplate(TemplateAppliedEventArgs e) | ||
| { | ||
| _disposables.Clear(); | ||
| base.OnApplyTemplate(e); | ||
|
|
||
| _inputTabButton = e.NameScope.Find<RadioButton>("InputTabButton"); | ||
| _helpTabButton = e.NameScope.Find<RadioButton>("HelpTabButton"); | ||
|
|
||
| _inputTabButton?.AddDisposableHandler(ToggleButton.IsCheckedChangedEvent, OnTabButtonIsCheckedChanged) | ||
| .DisposeWith(_disposables); | ||
|
|
||
| _helpTabButton?.AddDisposableHandler(ToggleButton.IsCheckedChangedEvent, OnTabButtonIsCheckedChanged) | ||
| .DisposeWith(_disposables); | ||
|
|
||
| if (_inputTabButton != null) | ||
| { | ||
| _inputTabButton.IsChecked = true; | ||
| } | ||
| } | ||
|
|
||
| private void OnTabButtonIsCheckedChanged(object? sender, RoutedEventArgs e) | ||
| { | ||
| if (_helpTabButton != null) | ||
| { | ||
| PseudoClasses.Set(HelpTabPseudoClass, _helpTabButton.IsChecked == true); | ||
| } | ||
| } | ||
|
|
||
| protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) | ||
| { | ||
| base.OnPropertyChanged(change); | ||
| if (change.Property == ErrorMessageProperty) | ||
| { | ||
| PseudoClasses.Set(HasErrorPseudoClass, !string.IsNullOrWhiteSpace(ErrorMessage)); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
198 changes: 198 additions & 0 deletions
198
src/Beutl.Controls/Styling/PropertyEditors/ExpressionEditorFlyoutPresenter.axaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| <ResourceDictionary xmlns="https://github.com/avaloniaui" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
| xmlns:lang="using:Beutl.Language" | ||
| xmlns:local="using:Beutl.Controls.PropertyEditors" | ||
| xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" | ||
| x:CompileBindings="True"> | ||
|
|
||
| <ControlTheme x:Key="{x:Type local:ExpressionEditorFlyoutPresenter}" TargetType="local:ExpressionEditorFlyoutPresenter"> | ||
| <Setter Property="Width" Value="360" /> | ||
| <Setter Property="ShowHideButtons" Value="True" /> | ||
| <Setter Property="HorizontalContentAlignment" Value="Stretch" /> | ||
| <Setter Property="VerticalContentAlignment" Value="Stretch" /> | ||
| <Setter Property="Background" Value="{DynamicResource FlyoutPresenterBackground}" /> | ||
| <Setter Property="BorderBrush" Value="{DynamicResource FlyoutBorderThemeBrush}" /> | ||
| <Setter Property="BorderThickness" Value="{StaticResource FlyoutBorderThemeThickness}" /> | ||
| <Setter Property="Padding" Value="0" /> | ||
| <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" /> | ||
| <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" /> | ||
| <Setter Property="CornerRadius" Value="{DynamicResource OverlayCornerRadius}" /> | ||
| <Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> | ||
| <Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" /> | ||
| <Setter Property="Template"> | ||
| <ControlTemplate> | ||
| <Border Padding="{DynamicResource FlyoutBorderThemePadding}" | ||
| Background="{TemplateBinding Background}" | ||
| BorderBrush="{TemplateBinding BorderBrush}" | ||
| BorderThickness="{TemplateBinding BorderThickness}" | ||
| CornerRadius="{TemplateBinding CornerRadius}"> | ||
| <Grid RowDefinitions="Auto,*,Auto"> | ||
| <!-- Tab Header and Close Button --> | ||
| <Grid Name="DragArea" | ||
| Height="40" | ||
| VerticalAlignment="Top" | ||
| Background="Transparent" | ||
| ColumnDefinitions="*,Auto"> | ||
| <WrapPanel Name="TabLayout" Margin="4,4,0,4"> | ||
| <RadioButton Name="InputTabButton" | ||
| IsChecked="True" | ||
| ToolTip.Tip="{x:Static lang:Strings.Expression_Input}"> | ||
| <ui:SymbolIcon Symbol="Edit" /> | ||
| </RadioButton> | ||
| <RadioButton Name="HelpTabButton" | ||
| ToolTip.Tip="{x:Static lang:Strings.Expression_Help}"> | ||
| <ui:SymbolIcon Symbol="Help" /> | ||
| </RadioButton> | ||
| </WrapPanel> | ||
|
|
||
| <Button Name="CloseButton" | ||
| Grid.Column="1" | ||
| Width="32" | ||
| Height="32" | ||
| Margin="4" | ||
| Padding="0" | ||
| HorizontalAlignment="Right" | ||
| HorizontalContentAlignment="Center" | ||
| VerticalContentAlignment="Center" | ||
| Theme="{StaticResource TransparentButton}"> | ||
| <ui:FontIcon FontFamily="{DynamicResource SymbolThemeFontFamily}" Glyph="" /> | ||
| </Button> | ||
| </Grid> | ||
|
|
||
| <!-- Content Area --> | ||
| <Grid Name="ContentArea" Grid.Row="1" Margin="8,0,8,8"> | ||
| <!-- Input Tab --> | ||
| <StackPanel Name="InputPanel" Spacing="8"> | ||
| <TextBlock Text="{x:Static lang:Strings.ExpressionHelp}" | ||
| TextWrapping="Wrap" | ||
| FontSize="12" | ||
| Foreground="{DynamicResource TextFillColorSecondaryBrush}" /> | ||
| <TextBox Name="ExpressionTextBox" | ||
| AcceptsReturn="True" | ||
| MinHeight="100" | ||
| MaxHeight="200" | ||
| TextWrapping="Wrap" | ||
| Watermark="Sin(Time * 2 * PI) * 100" | ||
| Text="{TemplateBinding ExpressionText, Mode=TwoWay}" | ||
| VerticalContentAlignment="Top" /> | ||
| <TextBlock Name="ErrorTextBlock" | ||
| Text="{TemplateBinding ErrorMessage}" | ||
| TextWrapping="Wrap" | ||
| Foreground="{DynamicResource SystemFillColorCriticalBrush}" | ||
| IsVisible="False" /> | ||
| </StackPanel> | ||
|
|
||
| <!-- Help Tab --> | ||
| <ScrollViewer Name="HelpPanel" MaxHeight="300" IsVisible="False"> | ||
| <StackPanel Spacing="12"> | ||
| <!-- Variables Section --> | ||
| <StackPanel Spacing="4"> | ||
| <TextBlock Text="{x:Static lang:Strings.Expression_Variables}" | ||
| FontWeight="Bold" /> | ||
| <TextBlock Text="{x:Static lang:Strings.Expression_Variables_Description}" | ||
| TextWrapping="Wrap" | ||
| FontFamily="Consolas, Menlo, monospace" | ||
| FontSize="11" /> | ||
| </StackPanel> | ||
|
|
||
| <!-- Functions Section --> | ||
| <StackPanel Spacing="4"> | ||
| <TextBlock Text="{x:Static lang:Strings.Expression_Functions}" | ||
| FontWeight="Bold" /> | ||
| <StackPanel Spacing="4"> | ||
| <TextBlock Text="{x:Static lang:Strings.Expression_Functions_Trigonometric}" | ||
| TextWrapping="Wrap" | ||
| FontFamily="Consolas, Menlo, monospace" | ||
| FontSize="11" /> | ||
| <TextBlock Text="{x:Static lang:Strings.Expression_Functions_Math}" | ||
| TextWrapping="Wrap" | ||
| FontFamily="Consolas, Menlo, monospace" | ||
| FontSize="11" /> | ||
| <TextBlock Text="{x:Static lang:Strings.Expression_Functions_Interpolation}" | ||
| TextWrapping="Wrap" | ||
| FontFamily="Consolas, Menlo, monospace" | ||
| FontSize="11" /> | ||
| <TextBlock Text="{x:Static lang:Strings.Expression_Functions_Utility}" | ||
| TextWrapping="Wrap" | ||
| FontFamily="Consolas, Menlo, monospace" | ||
| FontSize="11" /> | ||
| </StackPanel> | ||
| </StackPanel> | ||
|
|
||
| <!-- GetProperty Section --> | ||
| <StackPanel Spacing="4"> | ||
| <TextBlock Text="GetProperty" FontWeight="Bold" /> | ||
| <TextBlock Text="{x:Static lang:Strings.Expression_GetProperty_Description}" | ||
| TextWrapping="Wrap" | ||
| FontFamily="Consolas, Menlo, monospace" | ||
| FontSize="11" /> | ||
| </StackPanel> | ||
| </StackPanel> | ||
| </ScrollViewer> | ||
| </Grid> | ||
|
|
||
| <!-- Accept/Dismiss Buttons --> | ||
| <Border Grid.Row="2" | ||
| BorderBrush="{DynamicResource PickerFlyoutPresenterDivider}" | ||
| BorderThickness="0,1,0,0"> | ||
| <Panel Name="AcceptDismissContainer" | ||
| Height="{DynamicResource PickerAcceptDismissRegionHeight}" | ||
| IsVisible="False"> | ||
| <Grid ColumnDefinitions="*,*"> | ||
| <Button Name="AcceptButton" | ||
| Margin="4,4,2,4" | ||
| HorizontalAlignment="Stretch" | ||
| VerticalAlignment="Stretch" | ||
| Theme="{StaticResource FlyoutAcceptDismiss}"> | ||
| <ui:SymbolIcon FontSize="18" Symbol="Checkmark" /> | ||
| </Button> | ||
| <Button Name="DismissButton" | ||
| Grid.Column="1" | ||
| Margin="2,4,4,4" | ||
| HorizontalAlignment="Stretch" | ||
| VerticalAlignment="Stretch" | ||
| Theme="{StaticResource FlyoutAcceptDismiss}"> | ||
| <ui:SymbolIcon FontSize="16" Symbol="Dismiss" /> | ||
| </Button> | ||
| </Grid> | ||
| </Panel> | ||
| </Border> | ||
| </Grid> | ||
| </Border> | ||
| </ControlTemplate> | ||
| </Setter> | ||
|
|
||
| <Style Selector="^:acceptdismiss /template/ Panel#AcceptDismissContainer"> | ||
| <Setter Property="IsVisible" Value="True" /> | ||
| </Style> | ||
| <Style Selector="^:acceptdismiss /template/ Button#CloseButton"> | ||
| <Setter Property="IsVisible" Value="False" /> | ||
| </Style> | ||
|
|
||
| <Style Selector="^:has-error /template/ TextBlock#ErrorTextBlock"> | ||
| <Setter Property="IsVisible" Value="True" /> | ||
| </Style> | ||
|
|
||
| <!-- Help tab visibility --> | ||
| <Style Selector="^:help-tab /template/ StackPanel#InputPanel"> | ||
| <Setter Property="IsVisible" Value="False" /> | ||
| </Style> | ||
| <Style Selector="^:help-tab /template/ ScrollViewer#HelpPanel"> | ||
| <Setter Property="IsVisible" Value="True" /> | ||
| </Style> | ||
|
|
||
| <!-- Tab button styles --> | ||
| <Style Selector="^ /template/ WrapPanel#TabLayout"> | ||
| <Style Selector="^ > RadioButton"> | ||
| <Setter Property="Width" Value="32" /> | ||
| <Setter Property="Height" Value="32" /> | ||
| <Setter Property="Margin" Value="0,0,4,0" /> | ||
| <Setter Property="Theme" Value="{StaticResource ColorPickerTypeTransparentToggleButtonStyle}" /> | ||
|
|
||
| <Style Selector="^ > ui|FontIcon"> | ||
| <Setter Property="FontFamily" Value="{DynamicResource SymbolThemeFontFamily}" /> | ||
| </Style> | ||
| </Style> | ||
| </Style> | ||
| </ControlTheme> | ||
| </ResourceDictionary> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| using System.Reactive.Disposables; | ||
| using System.Reactive.Linq; | ||
|
|
||
| namespace Beutl.Reactive; | ||
|
|
||
| public static class ObservableExtensions | ||
| { | ||
| extension(Observable) | ||
| { | ||
| public static IObservable<T> ReturnThenNever<T>(T value) | ||
| { | ||
| return Observable.Create<T>(observer => | ||
| { | ||
| observer.OnNext(value); | ||
| return Disposable.Empty; | ||
| }); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.