Skip to content

Commit

Permalink
Add support for hiding messages too (#355)
Browse files Browse the repository at this point in the history
Bits of status messages that were omitted from #281. 

This lets extensions hide messages (and exposes the helper in the helper lib). 

It also adds support for displaying progress as a progress bar underneath the text of the status message. I'll need An Adult to help with the XAML, to re-template the InfoBar to allow a progress wheel in the icon instead, but for now? good enough. 

I'm doing this to unblock the next PR, which should add some rudimentary winget support.
  • Loading branch information
zadjii-msft authored Jan 23, 2025
1 parent 2d4bead commit 4d464bc
Show file tree
Hide file tree
Showing 8 changed files with 325 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,17 @@ public override IListItem[] GetItems()
}
],
},
new ListItem(new SendMessageCommand()) { Title = "I send messages" },
new ListItem(new SendMessageCommand())
{
Title = "I send lots of messages",
Subtitle = "Status messages can be used to provide feedback to the user in-app",
},
new SendSingleMessageItem(),
new ListItem(new IndeterminateProgressMessageCommand())
{
Title = "Do a thing with a spinner",
Subtitle = "Messages can have progress spinners, to indicate something is happening in the background",
},
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ protected virtual void FetchProperty(string propertyName)

switch (propertyName)
{
case nameof(Command):
this.Command = new(model.Command);
Name = model.Command?.Name ?? string.Empty;
UpdateProperty(nameof(Name));

break;
case nameof(Name):
this.Name = model.Command?.Name ?? string.Empty;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,32 @@ public sealed partial class CommandPaletteHost : IExtensionHost

private static readonly GlobalLogPageContext _globalLogPageContext = new();

private ulong _hostHwnd;

public ulong HostingHwnd => _hostHwnd;
public ulong HostingHwnd { get; private set; }

public string LanguageOverride => string.Empty;

public static ObservableCollection<LogMessageViewModel> LogMessages { get; } = new();

public ObservableCollection<StatusMessageViewModel> StatusMessages { get; } = new();
public static ObservableCollection<LogMessageViewModel> LogMessages { get; } = [];

private readonly IExtensionWrapper? _source;
public ObservableCollection<StatusMessageViewModel> StatusMessages { get; } = [];

public IExtensionWrapper? Extension => _source;
public IExtensionWrapper? Extension { get; }

private CommandPaletteHost()
{
}

public CommandPaletteHost(IExtensionWrapper source)
{
_source = source;
Extension = source;
}

public IAsyncAction ShowStatus(IStatusMessage message)
public IAsyncAction ShowStatus(IStatusMessage? message)
{
if (message == null)
{
return Task.CompletedTask.AsAsyncAction();
}

Debug.WriteLine(message.Message);

_ = Task.Run(() =>
Expand All @@ -53,13 +54,27 @@ public IAsyncAction ShowStatus(IStatusMessage message)
return Task.CompletedTask.AsAsyncAction();
}

public IAsyncAction HideStatus(IStatusMessage message)
public IAsyncAction HideStatus(IStatusMessage? message)
{
if (message == null)
{
return Task.CompletedTask.AsAsyncAction();
}

_ = Task.Run(() =>
{
ProcessHideStatusMessage(message);
});
return Task.CompletedTask.AsAsyncAction();
}

public IAsyncAction LogMessage(ILogMessage message)
public IAsyncAction LogMessage(ILogMessage? message)
{
if (message == null)
{
return Task.CompletedTask.AsAsyncAction();
}

Debug.WriteLine(message.Message);

_ = Task.Run(() =>
Expand All @@ -77,9 +92,9 @@ public void ProcessLogMessage(ILogMessage message)
var vm = new LogMessageViewModel(message, _globalLogPageContext);
vm.SafeInitializePropertiesSynchronous();

if (_source != null)
if (Extension != null)
{
vm.ExtensionPfn = _source.PackageFamilyName;
vm.ExtensionPfn = Extension.PackageFamilyName;
}

Task.Factory.StartNew(
Expand All @@ -94,12 +109,28 @@ public void ProcessLogMessage(ILogMessage message)

public void ProcessStatusMessage(IStatusMessage message)
{
// If this message is already in the list of messages, just bring it to the top
var oldVm = StatusMessages.Where(messageVM => messageVM.Model.Unsafe == message).FirstOrDefault();
if (oldVm != null)
{
Task.Factory.StartNew(
() =>
{
StatusMessages.Remove(oldVm);
StatusMessages.Add(oldVm);
},
CancellationToken.None,
TaskCreationOptions.None,
_globalLogPageContext.Scheduler);
return;
}

var vm = new StatusMessageViewModel(message, _globalLogPageContext);
vm.SafeInitializePropertiesSynchronous();

if (_source != null)
if (Extension != null)
{
vm.ExtensionPfn = _source.PackageFamilyName;
vm.ExtensionPfn = Extension.PackageFamilyName;
}

Task.Factory.StartNew(
Expand All @@ -112,8 +143,27 @@ public void ProcessStatusMessage(IStatusMessage message)
_globalLogPageContext.Scheduler);
}

public void SetHostHwnd(ulong hostHwnd)
public void ProcessHideStatusMessage(IStatusMessage message)
{
_hostHwnd = hostHwnd;
Task.Factory.StartNew(
() =>
{
try
{
var vm = StatusMessages.Where(messageVM => messageVM.Model.Unsafe == message).FirstOrDefault();
if (vm != null)
{
StatusMessages.Remove(vm);
}
}
catch
{
}
},
CancellationToken.None,
TaskCreationOptions.None,
_globalLogPageContext.Scheduler);
}

public void SetHostHwnd(ulong hostHwnd) => HostingHwnd = hostHwnd;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.UI.ViewModels.Models;

namespace Microsoft.CmdPal.UI.ViewModels;

public partial class ProgressViewModel : ExtensionObjectViewModel
{
public ExtensionObject<IProgressState> Model { get; }

public bool IsIndeterminate { get; private set; }

public uint ProgressPercent { get; private set; }

public double ProgressValue => ProgressPercent / 100.0;

public ProgressViewModel(IProgressState progress, IPageContext context)
: base(context)
{
Model = new(progress);
}

public override void InitializeProperties()
{
var model = Model.Unsafe;
if (model == null)
{
return; // throw?
}

IsIndeterminate = model.IsIndeterminate;
ProgressPercent = model.ProgressPercent;

model.PropChanged += Model_PropChanged;
}

private void Model_PropChanged(object sender, PropChangedEventArgs args)
{
try
{
FetchProperty(args.PropertyName);
}
catch (Exception ex)
{
PageContext.ShowException(ex);
}
}

protected virtual void FetchProperty(string propertyName)
{
var model = this.Model.Unsafe;
if (model == null)
{
return; // throw?
}

switch (propertyName)
{
case nameof(IsIndeterminate):
this.IsIndeterminate = model.IsIndeterminate;
break;
case nameof(ProgressPercent):
this.ProgressPercent = model.ProgressPercent;
UpdateProperty(nameof(ProgressValue));
break;
}

UpdateProperty(propertyName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,43 @@ namespace Microsoft.CmdPal.UI.ViewModels;

public partial class StatusMessageViewModel : ExtensionObjectViewModel
{
private readonly ExtensionObject<IStatusMessage> _model;
public ExtensionObject<IStatusMessage> Model { get; }

public string Message { get; private set; } = string.Empty;

public MessageState State { get; private set; } = MessageState.Info;

public string ExtensionPfn { get; set; } = string.Empty;

public ProgressViewModel? Progress { get; private set; }

public bool HasProgress => Progress != null;

// public bool IsIndeterminate => Progress != null && Progress.IsIndeterminate;
// public double ProgressValue => (Progress?.ProgressPercent ?? 0) / 100.0;
public StatusMessageViewModel(IStatusMessage message, IPageContext context)
: base(context)
{
_model = new(message);
Model = new(message);
}

public override void InitializeProperties()
{
var model = _model.Unsafe;
var model = Model.Unsafe;
if (model == null)
{
return; // throw?
}

Message = model.Message;
State = model.State;
var modelProgress = model.Progress;
if (modelProgress != null)
{
Progress = new(modelProgress, this.PageContext);
Progress.InitializeProperties();
UpdateProperty(nameof(HasProgress));
}

model.PropChanged += Model_PropChanged;
}
Expand All @@ -51,7 +64,7 @@ private void Model_PropChanged(object sender, PropChangedEventArgs args)

protected virtual void FetchProperty(string propertyName)
{
var model = this._model.Unsafe;
var model = this.Model.Unsafe;
if (model == null)
{
return; // throw?
Expand All @@ -65,6 +78,20 @@ protected virtual void FetchProperty(string propertyName)
case nameof(State):
this.State = model.State;
break;
case nameof(Progress):
var modelProgress = model.Progress;
if (modelProgress != null)
{
Progress = new(modelProgress, this.PageContext);
Progress.InitializeProperties();
}
else
{
Progress = null;
}

UpdateProperty(nameof(HasProgress));
break;
}

UpdateProperty(propertyName);
Expand Down
13 changes: 12 additions & 1 deletion src/modules/cmdpal/Microsoft.CmdPal.UI/ShellPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,18 @@
CornerRadius="{ThemeResource ControlCornerRadius}"
IsOpen="{x:Bind ViewModel.CurrentPage.HasStatusMessage, Mode=OneWay}"
Message="{x:Bind ViewModel.CurrentPage.MostRecentStatusMessage.Message, Mode=OneWay}"
Severity="{x:Bind ViewModel.CurrentPage.MostRecentStatusMessage.State, Mode=OneWay, Converter={StaticResource MessageStateToSeverityConverter}}" />
Severity="{x:Bind ViewModel.CurrentPage.MostRecentStatusMessage.State, Mode=OneWay, Converter={StaticResource MessageStateToSeverityConverter}}">

<InfoBar.Content>
<ProgressBar
Visibility="{x:Bind ViewModel.CurrentPage.MostRecentStatusMessage.HasProgress, Mode=OneWay}"
IsIndeterminate="{x:Bind ViewModel.CurrentPage.MostRecentStatusMessage.Progress.IsIndeterminate, Mode=OneWay}"
Value="{x:Bind ViewModel.CurrentPage.MostRecentStatusMessage.Progress.ProgressValue, Mode=OneWay}"
Margin="0,-20,0,0"
/>
<!--Margin="0,0,0,6" MaxWidth="200"/>-->
</InfoBar.Content>
</InfoBar>
</StackPanel>

<cpcontrols:ActionBar Grid.Row="1" CurrentPageViewModel="{x:Bind ViewModel.CurrentPage, Mode=OneWay}" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,9 @@ namespace Microsoft.CmdPal.Extensions.Helpers;

public class ExtensionHost
{
private static IExtensionHost? _host;
public static IExtensionHost? Host { get; private set; }

public static IExtensionHost? Host => _host;

public static void Initialize(IExtensionHost host)
{
_host = host;
}
public static void Initialize(IExtensionHost host) => Host = host;

/// <summary>
/// Fire-and-forget a log message to the Command Palette host app. Since
Expand Down Expand Up @@ -61,4 +56,21 @@ public static void ShowStatus(IStatusMessage message)
});
}
}

public static void HideStatus(IStatusMessage message)
{
if (Host != null)
{
_ = Task.Run(async () =>
{
try
{
await Host.HideStatus(message);
}
catch (Exception)
{
}
});
}
}
}
Loading

0 comments on commit 4d464bc

Please sign in to comment.