Skip to content

Commit

Permalink
Merge pull request #10 from TwelveBaud/unbreak
Browse files Browse the repository at this point in the history
  • Loading branch information
APIWT authored Sep 21, 2021
2 parents e2c9b4f + 51ed312 commit dc5baca
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 17 deletions.
8 changes: 8 additions & 0 deletions src/Client/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using Autofac;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using WillowTree.Sweetgum.Client.DependencyInjection;
Expand Down Expand Up @@ -35,6 +36,13 @@ public override void Initialize()
/// <inheritdoc cref="Application"/>
public override void OnFrameworkInitializationCompleted()
{
if (Design.IsDesignMode)
{
// The Visual Studio designer doesn't run our entry point, so DI and services haven't been set up.
// Do this now.
Dependencies.ConfigureServices();
}

var settingsManager = Dependencies.Container.Resolve<SettingsManager>();
settingsManager.Load();

Expand Down
21 changes: 17 additions & 4 deletions src/Client/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright (c) WillowTree, LLC. All rights reserved.
// </copyright>

using System;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading;
Expand All @@ -23,10 +24,10 @@ public sealed class MainWindowViewModel : ReactiveObject
public MainWindowViewModel()
{
this.NewWorkbookCommand = ReactiveCommand.CreateFromTask<Unit, WorkbookModel>(this.NewWorkbookAsync);
this.NewWorkbookSpecifyPathInteraction = new Interaction<Unit, string>();
this.NewWorkbookSpecifyPathInteraction = new Interaction<Unit, string?>();

this.LoadWorkbookCommand = ReactiveCommand.CreateFromTask<Unit, WorkbookModel>(this.LoadWorkbookAsync);
this.LoadWorkbookSpecifyPathInteraction = new Interaction<Unit, string>();
this.LoadWorkbookSpecifyPathInteraction = new Interaction<Unit, string?>();

this.OpenSettingsCommand = ReactiveCommand.Create(() => { });
}
Expand All @@ -49,19 +50,25 @@ public MainWindowViewModel()
/// <summary>
/// Gets the interaction to specify a path for a new workbook.
/// </summary>
public Interaction<Unit, string> NewWorkbookSpecifyPathInteraction { get; }
public Interaction<Unit, string?> NewWorkbookSpecifyPathInteraction { get; }

/// <summary>
/// Gets the interaction to specify a path for an existing workbook.
/// </summary>
public Interaction<Unit, string> LoadWorkbookSpecifyPathInteraction { get; }
public Interaction<Unit, string?> LoadWorkbookSpecifyPathInteraction { get; }

private async Task<WorkbookModel> NewWorkbookAsync(
Unit input,
CancellationToken cancellationToken)
{
var path = await this.NewWorkbookSpecifyPathInteraction.Handle(Unit.Default);

if (string.IsNullOrEmpty(path))
{
// We don't have access to the observable subscription or CTS here since it's eaten by Avalonia.
throw new TaskCanceledException();
}

return await WorkbookManager.NewAsync(path, cancellationToken);
}

Expand All @@ -71,6 +78,12 @@ private async Task<WorkbookModel> LoadWorkbookAsync(
{
var path = await this.LoadWorkbookSpecifyPathInteraction.Handle(Unit.Default);

if (string.IsNullOrEmpty(path))
{
// We don't have access to the observable subscription or CTS here since it's eaten by Avalonia.
throw new TaskCanceledException();
}

return await WorkbookManager.LoadAsync(path, cancellationToken);
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/Client/Views/ErrorDialog.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="150"
x:Class="WillowTree.Sweetgum.Client.Views.ErrorDialog"
Title="Sweetgum"
WindowStartupLocation="CenterOwner" ShowInTaskbar="False" CanResize="false"
>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Border Background="{DynamicResource ThemeBackgroundBrush}" Grid.Row="0" Grid.Column="0">
<TextBlock HorizontalAlignment="Center" TextAlignment="Left" Name="txtMessage" Text="Sorry, an error occurred." />
</Border>
<Border Background="{DynamicResource ThemeControlMidBrush}" Grid.Row="1" Grid.Column="0">
<Button HorizontalAlignment="Center" HorizontalContentAlignment="Center" MinWidth="100" Name="btnOk" Content="Ok" />
</Border>
</Grid>
</Window>
72 changes: 72 additions & 0 deletions src/Client/Views/ErrorDialog.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// <copyright file="ErrorDialog.axaml.cs" company="WillowTree, LLC">
// Copyright (c) WillowTree, LLC. All rights reserved.
// </copyright>

using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace WillowTree.Sweetgum.Client.Views
{
/// <summary>
/// A class for showing a simple error message.
/// </summary>
/// <remarks>This class does not derive from <see cref="WillowTree.Sweetgum.Client.BaseControls.Views.BaseWindow{TViewModel}"/> because
/// it only displays a simple string and doesn't interact with any model objects.</remarks>
public partial class ErrorDialog : Window
{
/// <summary>
/// Initializes a new instance of the <see cref="ErrorDialog"/> class.
/// </summary>
/// <param name="exception">The exception whose message to display.</param>
/// <param name="parent">The window that owns this dialog.</param>
public ErrorDialog(Exception exception, WindowBase parent)
{
this.Exception = exception;
this.Owner = parent;
this.InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
TextBlock block = this.Find<TextBlock>("txtMessage");
block.Text = this.Exception.Message;

this.Find<Button>("btnOk").Click += (s, e) => this.Close();

// We want to constrain the dialog to the message as closely as possible,
// but still be within our parent, as well as have a reasonable minimum size.
// Width should be 400 <= error message with a 16-pixel margin <= parent's width.
// Height should be just enough for the text, button, and margin.
block.Measure(new Size(Math.Max(400, parent.Width - 32), parent.Height - 62)); // Buttons are 30px high using Avalonia's WPF renderer by default.
Size desiredSize = block.DesiredSize;
this.Width = Math.Min(Math.Max(400, desiredSize.Width + 32), parent.Width);
this.Height = desiredSize.Height + 62;
}

/// <summary>
/// Initializes a new instance of the <see cref="ErrorDialog"/> class.
/// </summary>
/// <param name="message">The message to display.</param>
/// <param name="parent">The window that owns this dialog.</param>
public ErrorDialog(string message, WindowBase parent)
: this(new Exception(message), parent)
{
}

#pragma warning disable CS1591, CS8618, SA1600, SA1502
[Obsolete("Only used for Avalonia infrastructure.")]
public ErrorDialog() { }
#pragma warning restore CS1591, CS8618, SA1600, SA1502

/// <summary>
/// Gets the exception for the message shown in this dialog.
/// </summary>
public Exception Exception { get; }

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
18 changes: 16 additions & 2 deletions src/Client/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.ComponentModel;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Autofac;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
Expand Down Expand Up @@ -74,7 +75,7 @@ public static MainWindow Create()
};
var path = await dialog.ShowAsync(window);
context.SetOutput(string.IsNullOrWhiteSpace(path) ? string.Empty : path);
context.SetOutput(path);
});
window.BindInteraction(
Expand All @@ -89,7 +90,7 @@ public static MainWindow Create()
var path = await dialog.ShowAsync(window);
context.SetOutput(string.IsNullOrWhiteSpace(path) ? string.Empty : path);
context.SetOutput(path);
});
window.WhenAnyObservable(
Expand Down Expand Up @@ -149,6 +150,19 @@ public static MainWindow Create()
: 800;
})
.DisposeWith(disposables);
window.WhenAnyObservable(
view => view.ViewModel!.NewWorkbookCommand.ThrownExceptions,
view => view.ViewModel!.LoadWorkbookCommand.ThrownExceptions,
view => view.ViewModel!.OpenSettingsCommand.ThrownExceptions)
.Subscribe(async exception =>
{
if (exception is not TaskCanceledException)
{
await new ErrorDialog(exception, window).ShowDialog(window);
}
})
.DisposeWith(disposables);
});

return window;
Expand Down
29 changes: 19 additions & 10 deletions src/Client/Workbooks/ViewModels/WorkbookViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
Expand Down Expand Up @@ -49,6 +50,7 @@ public WorkbookViewModel(
this.programStateManager = programStateManager;

var isRenamingBehaviorSubject = new BehaviorSubject<bool>(false);
var deferredCanCreateItemBehaviorSubject = new BehaviorSubject<bool>(false);

this.isRenamingObservableAsPropertyHelper = isRenamingBehaviorSubject
.ToProperty(this, viewModel => viewModel.IsRenaming);
Expand Down Expand Up @@ -117,18 +119,20 @@ public WorkbookViewModel(

this.NewRequestInteraction = new Interaction<WorkbookModel, PathModel?>();

this.NewRequestCommand = ReactiveCommand.CreateFromTask(async () =>
{
var newRequestPath = await this.NewRequestInteraction.Handle(workbookModel);
if (newRequestPath != null)
this.NewRequestCommand = ReactiveCommand.CreateFromTask(
async () =>
{
workbookModel = workbookModel.NewRequest(newRequestPath);
this.WorkbookItems.Update(workbookModel);
}
var newRequestPath = await this.NewRequestInteraction.Handle(workbookModel);
return Unit.Default;
});
if (newRequestPath != null)
{
workbookModel = workbookModel.NewRequest(newRequestPath);
this.WorkbookItems.Update(workbookModel);
}
return Unit.Default;
},
deferredCanCreateItemBehaviorSubject);

this.OpenEnvironmentsCommand = ReactiveCommand.Create(() => new OpenEnvironmentsResult(
workbookModel,
Expand Down Expand Up @@ -161,6 +165,11 @@ public WorkbookViewModel(
this.SaveCommand,
openRequestCommand,
workbookState);

// This has to be constructed by hand rather than intuited from a lambda expression because Expression has different arity than Expression<Func<WorkbookViewModel, int>>
Expression watchChain = Expression.Property(Expression.Property(Expression.Property(Expression.Property(Expression.Parameter(typeof(WorkbookViewModel)), "WorkbookItems"), "FolderItems"), "Items"), "Count");
this.SubscribeToExpressionChain<WorkbookViewModel, int>(watchChain).Select(countProperty => countProperty.Value > 0).Subscribe(deferredCanCreateItemBehaviorSubject);
deferredCanCreateItemBehaviorSubject.OnNext(this.WorkbookItems.FolderItems.Items.Count > 0); // Reinject the current value once load completes.
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Client/Workbooks/Views/RequestWorkbookItem.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public RequestWorkbookItem()
.PointerReleased
.Select(_ => this.ViewModel?.RequestModel)
.Where(requestModel => requestModel != null)
.InvokeCommand(this, view => view.ViewModel!.OpenRequestCommand)
.InvokeCommand(this, view => view.ViewModel!.OpenRequestCommand!)
.DisposeWith(disposables);
});
}
Expand Down

0 comments on commit dc5baca

Please sign in to comment.