Skip to content

Commit

Permalink
Set up a control that's essentially the same as ngIf in Angular.
Browse files Browse the repository at this point in the history
  • Loading branch information
MeltyPlayer committed May 14, 2024
1 parent bdfc945 commit 713c281
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 70 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Metadata;

namespace uni.ui.avalonia.common.controls;

public class If : Control {
private object deferredContent_;
private Control control_;

public static readonly StyledProperty<bool> ConditionProperty
= AvaloniaProperty.Register<If, bool>(
"Condition");

public bool Condition {
get => this.GetValue(ConditionProperty);
set => this.SetValue(ConditionProperty, value);
}

[Content, TemplateContent]
public object DeferredContent {
get => this.deferredContent_;
set {
this.deferredContent_ = value;
if (this.Condition) {
this.DoLoad_(true);
}
}
}

public Control Control => this.control_;

static If() {
ConditionProperty.Changed.AddClassHandler<If>(
(c, e) => {
if (e.NewValue is bool v) {
c.DoLoad_(v);
}
});
}

protected override Size MeasureOverride(Size availableSize)
=> LayoutHelper.MeasureChild(this.control_, availableSize, default);

protected override Size ArrangeOverride(Size finalSize)
=> LayoutHelper.ArrangeChild(this.control_, finalSize, default);


private void DoLoad_(bool load) {
if ((this.control_ != null) == load)
return;

if (load) {
this.control_ = TemplateContent.Load(this.DeferredContent).Result;
((ISetLogicalParent) this.control_).SetParent(this);
this.VisualChildren.Add(this.control_);
this.LogicalChildren.Add(this.control_);
} else {
((ISetLogicalParent) this.control_).SetParent(null);
this.LogicalChildren.Clear();
this.VisualChildren.Remove(this.control_);
this.control_ = null;
}

this.InvalidateMeasure();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Collections.ObjectModel;

using fin.audio.io;
using fin.audio.io.importers.ogg;
using fin.image;
using fin.io;
using fin.io.bundles;
using fin.model.io;
using fin.scene;
using fin.util.asserts;

using grezzo.api;

using uni.ui.avalonia.resources;
using uni.ui.avalonia.ViewModels;

using IImage = Avalonia.Media.IImage;

namespace uni.ui.avalonia.common.treeViews {

// Top-level view model types
public class FileBundleTreeViewModel<T>
: ViewModelBase, IFilterTreeViewViewModel<T> {
public ObservableCollection<INode<T>> Nodes { get; protected set; }

public event EventHandler<INode<T>>? NodeSelected;

public void ChangeSelection(INode node)
=> this.NodeSelected?.Invoke(this, Asserts.AsA<INode<T>>(node));
}

public class FileBundleTreeViewModel : FileBundleTreeViewModel<IAnnotatedFileBundle> {
public FileBundleTreeViewModel() {
this.Nodes = [
new FileBundleDirectoryNode("Animals",
[
new FileBundleDirectoryNode("Mammals",
[
new FileBundleLeafNode("Lion", new CmbModelFileBundle("foo", new FinFile()).Annotate(null)),
new FileBundleLeafNode("Cat", new OggAudioFileBundle(new FinFile()).Annotate(null))
])
])
];
}
}

// Node types
public class FileBundleDirectoryNode(
string label,
ObservableCollection<INode<IAnnotatedFileBundle>>? subNodes = null)
: ViewModelBase, INode<IAnnotatedFileBundle> {
public ObservableCollection<INode<IAnnotatedFileBundle>>? SubNodes { get; }
= subNodes;

public IImage? Icon => null;

public string Label { get; } = label;
}

public class FileBundleLeafNode(string label, IAnnotatedFileBundle data)
: ViewModelBase, INode<IAnnotatedFileBundle> {
public ObservableCollection<INode<IAnnotatedFileBundle>>? SubNodes => null;

public static readonly IImage MUSIC_ICON
= EmbeddedResourceUtil.LoadAvaloniaImage("music");

public static readonly IImage PICTURE_ICON
= EmbeddedResourceUtil.LoadAvaloniaImage("picture");

public static readonly IImage MODEL_ICON
= EmbeddedResourceUtil.LoadAvaloniaImage("model");

public static readonly IImage SCENE_ICON
= EmbeddedResourceUtil.LoadAvaloniaImage("scene");

public IImage Icon => data.FileBundle switch {
IAudioFileBundle => MUSIC_ICON,
IImageFileBundle => PICTURE_ICON,
IModelFileBundle => MODEL_ICON,
ISceneFileBundle => SCENE_ICON,
};

public string Label { get; } = label;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:textboxes="clr-namespace:uni.ui.avalonia.common.textboxes"
xmlns:treeViews="clr-namespace:uni.ui.avalonia.common.treeViews"
xmlns:global="clr-namespace:"
xmlns:controls="clr-namespace:uni.ui.avalonia.common.controls"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="450"
x:Class="uni.ui.avalonia.common.treeViews.FilterTreeView"
x:DataType="treeViews:FileBundleTreeViewModel">
Expand Down Expand Up @@ -45,11 +47,12 @@
<TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding SubNodes}">
<StackPanel Orientation="Horizontal">
<Image IsVisible="{Binding Icon, Converter={x:Static ObjectConverters.IsNotNull}}"
Source="{Binding Icon}"
Margin="-24 0 4 0"
Height="16"
Width="16" />
<controls:If Condition="{Binding Icon, Converter={x:Static ObjectConverters.IsNotNull}}">
<Image Source="{Binding Icon}"
Margin="-24 0 4 0"
Height="16"
Width="16" />
</controls:If>
<TextBlock Text="{Binding Label}" />
</StackPanel>
</TreeDataTemplate>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,69 +43,4 @@ private void TreeView_OnSelectionChanged_(
filterTreeViewViewModel.ChangeSelection(selectedNode);
}
}

// Top-level view model types
public class FileBundleTreeViewModel<T>
: ViewModelBase, IFilterTreeViewViewModel<T> {
public ObservableCollection<INode<T>> Nodes { get; protected set; }

public event EventHandler<INode<T>>? NodeSelected;

public void ChangeSelection(INode node)
=> this.NodeSelected?.Invoke(this, Asserts.AsA<INode<T>>(node));
}

public class FileBundleTreeViewModel : FileBundleTreeViewModel<IAnnotatedFileBundle> {
public FileBundleTreeViewModel() {
this.Nodes = [
new ParentNode("Animals",
[
new ParentNode("Mammals",
[
new LeafNode("Lion", new CmbModelFileBundle("foo", new FinFile()).Annotate(null)),
new LeafNode("Cat", new OggAudioFileBundle(new FinFile()).Annotate(null))
])
])
];
}
}

// Node types
public class ParentNode(
string label,
ObservableCollection<INode<IAnnotatedFileBundle>>? subNodes = null)
: ViewModelBase, INode<IAnnotatedFileBundle> {
public ObservableCollection<INode<IAnnotatedFileBundle>>? SubNodes { get; }
= subNodes;

public IImage? Icon => null;

public string Label { get; } = label;
}

public class LeafNode(string label, IAnnotatedFileBundle data)
: ViewModelBase, INode<IAnnotatedFileBundle> {
public ObservableCollection<INode<IAnnotatedFileBundle>>? SubNodes => null;

public static readonly IImage MUSIC_ICON
= EmbeddedResourceUtil.LoadAvaloniaImage("music");

public static readonly IImage PICTURE_ICON
= EmbeddedResourceUtil.LoadAvaloniaImage("picture");

public static readonly IImage MODEL_ICON
= EmbeddedResourceUtil.LoadAvaloniaImage("model");

public static readonly IImage SCENE_ICON
= EmbeddedResourceUtil.LoadAvaloniaImage("scene");

public IImage? Icon => data.FileBundle switch {
IAudioFileBundle => MUSIC_ICON,
IImageFileBundle => PICTURE_ICON,
IModelFileBundle => MODEL_ICON,
ISceneFileBundle => SCENE_ICON,
};

public string Label { get; } = label;
}
}

0 comments on commit 713c281

Please sign in to comment.