mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 02:06:36 +02:00
[New Module] Workspaces (#34324)
* spell checker
* Adding OOBE Projects page
* changed the default hotkey
* module interface
* rename projects editor
* bug report tool
* installer
* gpo
* exit event constant
* extend search for projects by search over the containing apps' names
* [Projects] fix grammatical issue #43 (1 app - many apps)
* [Projects] Editor: Main page: fix layout if there are many apps, launch button not disappearing on the right side
* dsc
* github
* pipeline
* guid prefix
* [Projects] fixing general settings gpo handling in runner + minor changes
* arm build fix
* Do not allow saving project if name or applist is empty. Also minor UI changes
* version
* editor version
* spellcheck
* editor dll signing
* update projects names to filter them out
* shortcut saving fix
* [Projects] Editor: brining the highlighted app's icon into the foreground. + minor UI fixes
* spell checker
* spellcheck
* [Projects] re-implementing icon size calculation to have similar sized icons for every app.
* [projects] Adding info message for cases: there are no projects or no results for the search
* [Projects] Adding Edit button to the popup. + minor changes
* [Projects] Making popup having rounded corners
* changed "no projects" text color and position
* remove opening the first proj
* fix placing windows of the same app in the project
* [Projects] bringing back the breadcrumb on the editor page. Make it clickable.
* [Projects] optimizing click handlers
* [Projects] Removing not selected apps on save
* moved on thread executor to common
* moved display utils
* added convert rect
* unsigned monitor number
* set awareness
* app placement
* [Projects] Re-implementing preview drawing - one common image
* [Projects] fix boundary calculation, use DPI aware values
* fix launching with command line args
* Fix ARM64 CI build
* launch packaged apps using names when possible
* spell-check
* update packaged apps path
* projects editor single instance
* [Projects] Add Select all checkbox, Delete selected button
* Add Checkbox for per monitor selection
* modifying highlight in preview
* spell checker
* logs
* exclude help windows
https://github.com/JaneaSystems/PowerToys-DevProjects/issues/49
* Add intermediate step to project creation
* minor bugfix
* mutex fix
* modifying highlight for minimized apps
* Fixing bug: re-draw the preview on app deletion in the editor
* Adding helper class for getting the right bounds for screens
* spell checker
* spell checker
* Minor fixes in the capture dialog
* get dpi unaware screen bounds
* refactoring: added utils
* changed window filter
https://github.com/JaneaSystems/PowerToys-DevProjects/issues/2
* clean up
* refactoring
* projects common lib
* localizable default project prefix
* launcher resources
* clean up
* change snapshot project saving
https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14
* changed project data
https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14
* changed project creation save-cancel handles
https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14
* spell-check
* Remove checkboxes, delete feature
* remove unused from the project
* get command line args in the snapshot
* minimized settings snap fix
* set window property after launching
* FZ: ignore projects launched windows
* Implementing major new features: remove button, position manipulation, arguments, admin, minimized, maximized
* modifying colors
* launcher project filters
* clean up
* Hide Admin checkbox
* hide WIP
* spell-check
* Revert "Hide Admin checkbox"
This reverts commit 3036df9d7f.
* get app elevated property
* Implementing Launch and Edit feature
* fixing: update of listed projects on the main page after hitting save in editor
* Fix for packaged app's icons
* fixing scroll speed issue
* change scroll speed to 15
* launch elevated apps
* minor fixes
* minor fix
* enhancing shortcut handling
* can-launch-elevated check
* projects module interface telemetry
* Implementing store of setting "order by".
* minor string correction
* moved projects data parsing
* telemetry
* add move apps checkbox
* notification about elevated apps
* restart unelevated
* move existing windows
* keep opened windows at the same positions
* handle powertoys settings
* use common theme
* fix corrupted data: project id and monitor id
* project launch on "launch and edit"
* clean up
* show screen numbers instead of monitor names
* launcher error messages
* fix default shortcut
* Adding launch button to projects settings, dashboard and flyout
* Adding new app which is launched when launching a project. It shows the status of the launch process
* spell checker
* Renaming Projects to App Layouts. Replacing only string values, not the variable names
* Re-ordering modules after Renaming Projects + spell checker
* setting window size according to the screen (making it bigger)
* commenting out feature "move apps if exist"
* spell checker
* Add ProjectsLauncherUI to signing
* opening apps in minimized state which are placed on a monitor, which is not found at the moment of launching
* consistent file name
* removed unused sln
* telemetry: create event
* WindowPosition comparison
* telemetry: edit event
* fix muted Launch as admin checkbox
* telemetry: delete event
* updated Edit telemetry event
* added invoke point to launcher args
* added utils
* parse invoke point
* replaced tuple with struct
* telemetry: launch event
* MonitorRect comparison
* resources
* rename: folders
* remove outdated
* rename: window property
* rename: files and folders
* rename: common data structures
* rename: telemetry namespace
* rename: workspaces data
* rename ProjectsLib -> WorkspacesLib
* rename: gpo
* rename: settings
* rename: launcher UI
* rename: other
* rename: pt run
* rename: fz
* rename: module interface
* rename: icon
* rename: snapshot tool
* rename: editor
* rename: common files
* rename: launcher
* rename: editor resources
* fix empty file crash
* rename: json
* rename: module interface
* fix custom actions build
* added launch editor event constant
* xaml formatting
* Add missing method defition to interop::Constants idl
Remove Any CPU config
* more .sln cleanup
* [Run][PowerToys] Fix Workspaces utility (#34336)
polished workspaces utility
* build fix - align CppWinRT version
* address PR comment: fix isdigit
* indentation
* address PR comment: rename function
* address PR comment: changed version for workspaces and revision
* added supported version definition
* addressPR comment: use BringToForeground
* address PR comments: updated projects
* address PR comment: uncomment gpo in settings
* address PR comment: rename oobe view
* update OOBE image with current module name
* moved AppUtils
* launching with AppUserModel.ID
* fixed module order in settings
* fix xaml formatting
* [Workspaces] Close launcher if there are failed launches. Plus adding new spinner gif
* fix topmost LauncherUI
* clean up
* UI closing
* BugReportTool - omit cmd arg data
* Delete icon on workspace removal
* Adding cancellation to launcher UI.
* reordered launching
* fix terminating UI
* Removing old shortcut on workspace renaming
* Sentence case labels
* get process path without waiting
* comment out unused
* remove unused argument
* logs
* New icon
* fix launch and edit for the new project
* fix launch and edit: save new project
* Update exe icons
---------
Co-authored-by: donlaci <laszlo@janeasystems.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
Co-authored-by: Davide Giacometti <davide.giacometti@outlook.it>
Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
9
src/modules/Workspaces/WorkspacesEditor/App.config
Normal file
9
src/modules/Workspaces/WorkspacesEditor/App.config
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
|
||||
</startup>
|
||||
<runtime>
|
||||
<AppContextSwitchOverrides value = "Switch.System.Windows.DoNotScaleForDpiChanges=false"/>
|
||||
</runtime>
|
||||
</configuration>
|
||||
20
src/modules/Workspaces/WorkspacesEditor/App.xaml
Normal file
20
src/modules/Workspaces/WorkspacesEditor/App.xaml
Normal file
@@ -0,0 +1,20 @@
|
||||
<Application
|
||||
x:Class="WorkspacesEditor.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:WorkspacesEditor"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
Exit="OnExit"
|
||||
Startup="OnStartup">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ThemeResources />
|
||||
<ui:XamlControlsResources />
|
||||
<ResourceDictionary Source="pack://application:,,,/Styles/ButtonStyles.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style x:Key="HeadingTextBlock" TargetType="TextBlock" />
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
120
src/modules/Workspaces/WorkspacesEditor/App.xaml.cs
Normal file
120
src/modules/Workspaces/WorkspacesEditor/App.xaml.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
// 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 System;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using Common.UI;
|
||||
using ManagedCommon;
|
||||
using WorkspacesEditor.Utils;
|
||||
using WorkspacesEditor.ViewModels;
|
||||
|
||||
namespace WorkspacesEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application, IDisposable
|
||||
{
|
||||
private static Mutex _instanceMutex;
|
||||
|
||||
public static WorkspacesEditorIO WorkspacesEditorIO { get; private set; }
|
||||
|
||||
private MainWindow _mainWindow;
|
||||
|
||||
private MainViewModel _mainViewModel;
|
||||
|
||||
public static ThemeManager ThemeManager { get; set; }
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
public App()
|
||||
{
|
||||
WorkspacesEditorIO = new WorkspacesEditorIO();
|
||||
}
|
||||
|
||||
private void OnStartup(object sender, StartupEventArgs e)
|
||||
{
|
||||
Logger.InitializeLogger("\\Workspaces\\Logs");
|
||||
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||||
|
||||
const string appName = "Local\\PowerToys_Workspaces_Editor_InstanceMutex";
|
||||
bool createdNew;
|
||||
_instanceMutex = new Mutex(true, appName, out createdNew);
|
||||
if (!createdNew)
|
||||
{
|
||||
Logger.LogWarning("Another instance of Workspaces Editor is already running. Exiting this instance.");
|
||||
_instanceMutex = null;
|
||||
Shutdown(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PowerToys.GPOWrapperProjection.GPOWrapper.GetConfiguredWorkspacesEnabledValue() == PowerToys.GPOWrapperProjection.GpoRuleConfigured.Disabled)
|
||||
{
|
||||
Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
|
||||
Shutdown(0);
|
||||
return;
|
||||
}
|
||||
|
||||
ThemeManager = new ThemeManager(this);
|
||||
|
||||
if (_mainViewModel == null)
|
||||
{
|
||||
_mainViewModel = new MainViewModel(WorkspacesEditorIO);
|
||||
}
|
||||
|
||||
var parseResult = WorkspacesEditorIO.ParseWorkspaces(_mainViewModel);
|
||||
|
||||
// normal start of editor
|
||||
if (_mainWindow == null)
|
||||
{
|
||||
_mainWindow = new MainWindow(_mainViewModel);
|
||||
}
|
||||
|
||||
// reset main window owner to keep it on the top
|
||||
_mainWindow.ShowActivated = true;
|
||||
_mainWindow.Topmost = true;
|
||||
_mainWindow.Show();
|
||||
|
||||
// we can reset topmost flag after it's opened
|
||||
_mainWindow.Topmost = false;
|
||||
}
|
||||
|
||||
private void OnExit(object sender, ExitEventArgs e)
|
||||
{
|
||||
if (_instanceMutex != null)
|
||||
{
|
||||
_instanceMutex.ReleaseMutex();
|
||||
}
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
|
||||
{
|
||||
Logger.LogError("Unhandled exception occurred", args.ExceptionObject as Exception);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
ThemeManager?.Dispose();
|
||||
_instanceMutex?.Dispose();
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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 System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace WorkspacesEditor.Controls
|
||||
{
|
||||
public class ResetIsEnabled : ContentControl
|
||||
{
|
||||
static ResetIsEnabled()
|
||||
{
|
||||
IsEnabledProperty.OverrideMetadata(
|
||||
typeof(ResetIsEnabled),
|
||||
new UIPropertyMetadata(
|
||||
defaultValue: true,
|
||||
propertyChangedCallback: (_, __) => { },
|
||||
coerceValueCallback: (_, x) => x));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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 System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace WorkspacesEditor.Converters
|
||||
{
|
||||
public class BooleanToInvertedVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if ((bool)value)
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
return Visibility.Visible;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/modules/Workspaces/WorkspacesEditor/Data/InvokePoint.cs
Normal file
14
src/modules/Workspaces/WorkspacesEditor/Data/InvokePoint.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
// 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.
|
||||
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
/* sync with workspaces-common */
|
||||
public enum InvokePoint
|
||||
{
|
||||
EditorButton = 0,
|
||||
Shortcut,
|
||||
LaunchAndEdit,
|
||||
}
|
||||
}
|
||||
96
src/modules/Workspaces/WorkspacesEditor/Data/ProjectData.cs
Normal file
96
src/modules/Workspaces/WorkspacesEditor/Data/ProjectData.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using Workspaces.Data;
|
||||
using static WorkspacesEditor.Data.ProjectData;
|
||||
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class ProjectData : WorkspacesEditorData<ProjectWrapper>
|
||||
{
|
||||
public struct ApplicationWrapper
|
||||
{
|
||||
public struct WindowPositionWrapper
|
||||
{
|
||||
public int X { get; set; }
|
||||
|
||||
public int Y { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
public string Application { get; set; }
|
||||
|
||||
public string ApplicationPath { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string PackageFullName { get; set; }
|
||||
|
||||
public string AppUserModelId { get; set; }
|
||||
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
public bool CanLaunchElevated { get; set; }
|
||||
|
||||
public bool Minimized { get; set; }
|
||||
|
||||
public bool Maximized { get; set; }
|
||||
|
||||
public WindowPositionWrapper Position { get; set; }
|
||||
|
||||
public int Monitor { get; set; }
|
||||
}
|
||||
|
||||
public struct MonitorConfigurationWrapper
|
||||
{
|
||||
public struct MonitorRectWrapper
|
||||
{
|
||||
public int Top { get; set; }
|
||||
|
||||
public int Left { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
public string InstanceId { get; set; }
|
||||
|
||||
public int MonitorNumber { get; set; }
|
||||
|
||||
public int Dpi { get; set; }
|
||||
|
||||
public MonitorRectWrapper MonitorRectDpiAware { get; set; }
|
||||
|
||||
public MonitorRectWrapper MonitorRectDpiUnaware { get; set; }
|
||||
}
|
||||
|
||||
public struct ProjectWrapper
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public long CreationTime { get; set; }
|
||||
|
||||
public long LastLaunchedTime { get; set; }
|
||||
|
||||
public bool IsShortcutNeeded { get; set; }
|
||||
|
||||
public bool MoveExistingWindows { get; set; }
|
||||
|
||||
public List<MonitorConfigurationWrapper> MonitorConfiguration { get; set; }
|
||||
|
||||
public List<ApplicationWrapper> Applications { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 WorkspacesEditor.Utils;
|
||||
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class TempProjectData : ProjectData
|
||||
{
|
||||
public static string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return FolderUtils.DataFolder() + "\\temp-workspaces.json";
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteTempFile()
|
||||
{
|
||||
if (System.IO.File.Exists(File))
|
||||
{
|
||||
System.IO.File.Delete(File);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using Workspaces.Data;
|
||||
using WorkspacesEditor.Utils;
|
||||
using static WorkspacesEditor.Data.ProjectData;
|
||||
using static WorkspacesEditor.Data.WorkspacesData;
|
||||
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class WorkspacesData : WorkspacesEditorData<WorkspacesListWrapper>
|
||||
{
|
||||
public string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return FolderUtils.DataFolder() + "\\workspaces.json";
|
||||
}
|
||||
}
|
||||
|
||||
public struct WorkspacesListWrapper
|
||||
{
|
||||
public List<ProjectWrapper> Workspaces { get; set; }
|
||||
}
|
||||
|
||||
public enum OrderBy
|
||||
{
|
||||
LastViewed = 0,
|
||||
Created = 1,
|
||||
Name = 2,
|
||||
Unknown = 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 System.Text.Json;
|
||||
using WorkspacesEditor.Utils;
|
||||
|
||||
namespace Workspaces.Data
|
||||
{
|
||||
public class WorkspacesEditorData<T>
|
||||
{
|
||||
protected JsonSerializerOptions JsonOptions
|
||||
{
|
||||
get
|
||||
{
|
||||
return new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
WriteIndented = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public T Read(string file)
|
||||
{
|
||||
IOUtils ioUtils = new IOUtils();
|
||||
string data = ioUtils.ReadFile(file);
|
||||
return JsonSerializer.Deserialize<T>(data, JsonOptions);
|
||||
}
|
||||
|
||||
public string Serialize(T data)
|
||||
{
|
||||
return JsonSerializer.Serialize(data, JsonOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/modules/Workspaces/WorkspacesEditor/HeadingTextBlock.cs
Normal file
31
src/modules/Workspaces/WorkspacesEditor/HeadingTextBlock.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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 System.Windows;
|
||||
using System.Windows.Automation.Peers;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace WorkspacesEditor
|
||||
{
|
||||
public class HeadingTextBlock : TextBlock
|
||||
{
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new HeadingTextBlockAutomationPeer(this);
|
||||
}
|
||||
|
||||
internal sealed class HeadingTextBlockAutomationPeer : TextBlockAutomationPeer
|
||||
{
|
||||
public HeadingTextBlockAutomationPeer(HeadingTextBlock owner)
|
||||
: base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.Header;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
322
src/modules/Workspaces/WorkspacesEditor/MainPage.xaml
Normal file
322
src/modules/Workspaces/WorkspacesEditor/MainPage.xaml
Normal file
@@ -0,0 +1,322 @@
|
||||
<Page
|
||||
x:Class="WorkspacesEditor.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:WorkspacesEditor.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:WorkspacesEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:props="clr-namespace:WorkspacesEditor.Properties"
|
||||
Title="MainPage"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BoolToVis" />
|
||||
<converters:BooleanToInvertedVisibilityConverter x:Key="BooleanToInvertedVisibilityConverter" />
|
||||
<Thickness x:Key="ContentDialogPadding">24,16,0,24</Thickness>
|
||||
<Thickness x:Key="ContentDialogCommandSpaceMargin">0,24,24,0</Thickness>
|
||||
<Style x:Key="DeleteButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{DynamicResource TertiaryBackgroundBrush}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border
|
||||
x:Name="border"
|
||||
Padding="26,6,26,6"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="Transparent">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource TitleBarSecondaryForegroundBrush}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="{DynamicResource TitleBarSecondaryForegroundBrush}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<local:HeadingTextBlock
|
||||
x:Name="WorkspacesHeaderBlock"
|
||||
Grid.Row="0"
|
||||
Margin="40,20,40,20"
|
||||
AutomationProperties.HeadingLevel="Level1"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.Workspaces}" />
|
||||
|
||||
<Button
|
||||
x:Name="NewProjectButton"
|
||||
Grid.Row="0"
|
||||
Height="36"
|
||||
Margin="0,20,40,20"
|
||||
Padding="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
AutomationProperties.Name="{x:Static props:Resources.CreateWorkspace}"
|
||||
Click="NewProjectButton_Click"
|
||||
Style="{StaticResource AccentButtonStyle}"
|
||||
TabIndex="3">
|
||||
<StackPanel Margin="12,8,12,8" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
AutomationProperties.Name="{x:Static props:Resources.CreateWorkspace}"
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
Foreground="{DynamicResource AccentButtonForeground}"
|
||||
Text="" />
|
||||
<TextBlock
|
||||
Margin="12,-3,0,0"
|
||||
Foreground="{DynamicResource AccentButtonForeground}"
|
||||
Text="{x:Static props:Resources.CreateWorkspace}" />
|
||||
</StackPanel>
|
||||
<Button.Effect>
|
||||
<DropShadowEffect
|
||||
BlurRadius="6"
|
||||
Opacity="0.32"
|
||||
ShadowDepth="1" />
|
||||
</Button.Effect>
|
||||
</Button>
|
||||
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Margin="40,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
BorderThickness="2"
|
||||
CornerRadius="5">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Grid>
|
||||
<TextBox
|
||||
x:Name="SearchTextBox"
|
||||
Width="320"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PrimaryBorderBrush}"
|
||||
Text="{Binding SearchTerm, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
ToolTip="{x:Static props:Resources.SearchExplanation}" />
|
||||
<TextBlock
|
||||
Margin="10,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource SecondaryForegroundBrush}"
|
||||
IsHitTestVisible="False"
|
||||
Text="{x:Static props:Resources.Search}"
|
||||
ToolTip="{x:Static props:Resources.SearchExplanation}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Text, ElementName=SearchTextBox}" Value="">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Margin="-50,0,34,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Search}"
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
Foreground="{DynamicResource SecondaryForegroundBrush}"
|
||||
Text="" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0,0,40,0"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="10,0,10,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.SortBy}" />
|
||||
<ComboBox
|
||||
Width="140"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PrimaryBorderBrush}"
|
||||
SelectedIndex="{Binding OrderByIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||
<ComboBoxItem Content="{x:Static props:Resources.LastLaunched}" />
|
||||
<ComboBoxItem Content="{x:Static props:Resources.Created}" />
|
||||
<ComboBoxItem Content="{x:Static props:Resources.Name}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20"
|
||||
Foreground="{DynamicResource SecondaryForegroundBrush}"
|
||||
Text="{Binding EmptyWorkspacesViewMessage, UpdateSourceTrigger=PropertyChanged}"
|
||||
TextAlignment="Center"
|
||||
Visibility="{Binding IsWorkspacesViewEmpty, Mode=OneWay, Converter={StaticResource BoolToVis}, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ScrollViewer
|
||||
Grid.Row="2"
|
||||
Margin="40,15,40,40"
|
||||
VerticalContentAlignment="Stretch"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
Visibility="{Binding IsWorkspacesViewEmpty, Mode=OneWay, Converter={StaticResource BooleanToInvertedVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}">
|
||||
<ItemsControl ItemsSource="{Binding WorkspacesView, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
IsItemsHost="True"
|
||||
Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="models:Project">
|
||||
<Button
|
||||
x:Name="EditButton"
|
||||
Margin="0,12,0,0"
|
||||
Padding="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Edit}"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
Click="EditButtonClicked">
|
||||
<Border
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
CornerRadius="5">
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="110" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
Margin="12,14,10,10"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
Margin="0,0,0,8"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Text="{Binding Name, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<StackPanel
|
||||
Margin="0,0,0,8"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Image Height="20" Source="{Binding PreviewIcons, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<TextBlock
|
||||
Margin="6,0,4,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding AppsCountString}" />
|
||||
</StackPanel>
|
||||
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,3,10,0"
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="" />
|
||||
<TextBlock Text="{Binding LastLaunched, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Margin="12,12,12,12"
|
||||
Orientation="Vertical">
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="MoreButton"
|
||||
HorizontalAlignment="Right"
|
||||
Click="MoreButton_Click"
|
||||
Style="{StaticResource IconOnlyButtonStyle}">
|
||||
<TextBlock
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="" />
|
||||
</Button>
|
||||
<Popup
|
||||
AllowsTransparency="True"
|
||||
IsOpen="{Binding IsPopupVisible, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Placement="Left"
|
||||
PlacementTarget="{Binding ElementName=MoreButton}"
|
||||
StaysOpen="False">
|
||||
<Grid Background="{DynamicResource PrimaryBackgroundBrush}">
|
||||
<Grid.OpacityMask>
|
||||
<VisualBrush Visual="{Binding ElementName=OpacityBorder}" />
|
||||
</Grid.OpacityMask>
|
||||
<Border
|
||||
x:Name="OpacityBorder"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Black"
|
||||
CornerRadius="5" />
|
||||
<StackPanel Background="{DynamicResource PrimaryBackgroundBrush}" Orientation="Vertical">
|
||||
<Button
|
||||
AutomationProperties.Name="{x:Static props:Resources.Edit}"
|
||||
Click="EditButtonClicked"
|
||||
Style="{StaticResource DeleteButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
AutomationProperties.Name="{x:Static props:Resources.Edit}"
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="" />
|
||||
<TextBlock
|
||||
Margin="10,0,0,0"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Edit}"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.Edit}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
AutomationProperties.Name="{x:Static props:Resources.Delete}"
|
||||
Click="DeleteButtonClicked"
|
||||
Style="{StaticResource DeleteButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
AutomationProperties.Name="{x:Static props:Resources.Delete}"
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="" />
|
||||
<TextBlock
|
||||
Margin="10,0,0,0"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Delete}"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.Delete}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Popup>
|
||||
</StackPanel>
|
||||
<Button
|
||||
Margin="0,6,0,0"
|
||||
Padding="20,4,20,4"
|
||||
HorizontalAlignment="Right"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Launch}"
|
||||
Background="{DynamicResource TertiaryBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource SecondaryBorderBrush}"
|
||||
BorderThickness="1"
|
||||
Click="LaunchButton_Click"
|
||||
Content="{x:Static props:Resources.Launch}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Page>
|
||||
63
src/modules/Workspaces/WorkspacesEditor/MainPage.xaml.cs
Normal file
63
src/modules/Workspaces/WorkspacesEditor/MainPage.xaml.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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 System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using WorkspacesEditor.Models;
|
||||
using WorkspacesEditor.ViewModels;
|
||||
|
||||
namespace WorkspacesEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainPage.xaml
|
||||
/// </summary>
|
||||
public partial class MainPage : Page
|
||||
{
|
||||
private MainViewModel _mainViewModel;
|
||||
|
||||
public MainPage(MainViewModel mainViewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
_mainViewModel = mainViewModel;
|
||||
this.DataContext = _mainViewModel;
|
||||
}
|
||||
|
||||
private /*async*/ void NewProjectButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_mainViewModel.EnterSnapshotMode(false);
|
||||
}
|
||||
|
||||
private void EditButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_mainViewModel.CloseAllPopups();
|
||||
Button button = sender as Button;
|
||||
Project selectedProject = button.DataContext as Project;
|
||||
_mainViewModel.EditProject(selectedProject);
|
||||
}
|
||||
|
||||
private void DeleteButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
Button button = sender as Button;
|
||||
Project selectedProject = button.DataContext as Project;
|
||||
_mainViewModel.DeleteProject(selectedProject);
|
||||
}
|
||||
|
||||
private void MoreButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
Button button = sender as Button;
|
||||
Project project = button.DataContext as Project;
|
||||
project.IsPopupVisible = true;
|
||||
}
|
||||
|
||||
private void LaunchButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
Button button = sender as Button;
|
||||
Project project = button.DataContext as Project;
|
||||
_mainViewModel.LaunchProject(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml
Normal file
29
src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml
Normal file
@@ -0,0 +1,29 @@
|
||||
<Window
|
||||
x:Class="WorkspacesEditor.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
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:props="clr-namespace:WorkspacesEditor.Properties"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
x:Name="WorkspacesMainWindow"
|
||||
Title="{x:Static props:Resources.MainTitle}"
|
||||
MinWidth="700"
|
||||
MinHeight="680"
|
||||
ui:TitleBar.Background="{DynamicResource PrimaryBackgroundBrush}"
|
||||
ui:TitleBar.InactiveBackground="{DynamicResource TertiaryBackgroundBrush}"
|
||||
ui:TitleBar.IsIconVisible="True"
|
||||
ui:WindowHelper.UseModernWindowStyle="True"
|
||||
AutomationProperties.Name="Workspaces Editor"
|
||||
Background="{DynamicResource PrimaryBackgroundBrush}"
|
||||
Closing="OnClosing"
|
||||
ContentRendered="OnContentRendered"
|
||||
ResizeMode="CanResize"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
mc:Ignorable="d">
|
||||
<Border BorderThickness="1" CornerRadius="20">
|
||||
<Grid Margin="0,10,0,0">
|
||||
<Frame x:Name="ContentFrame" NavigationUIVisibility="Hidden" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
||||
71
src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs
Normal file
71
src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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 System;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using ManagedCommon;
|
||||
using WorkspacesEditor.Utils;
|
||||
using WorkspacesEditor.ViewModels;
|
||||
|
||||
namespace WorkspacesEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainViewModel MainViewModel { get; set; }
|
||||
|
||||
private static MainPage _mainPage;
|
||||
|
||||
public MainWindow(MainViewModel mainViewModel)
|
||||
{
|
||||
MainViewModel = mainViewModel;
|
||||
mainViewModel.SetMainWindow(this);
|
||||
|
||||
WindowInteropHelper windowInteropHelper = new WindowInteropHelper(this);
|
||||
System.Windows.Forms.Screen screen = System.Windows.Forms.Screen.FromHandle(windowInteropHelper.Handle);
|
||||
double dpi = MonitorHelper.GetScreenDpiFromScreen(screen);
|
||||
this.Height = screen.WorkingArea.Height / dpi * 0.90;
|
||||
this.Width = screen.WorkingArea.Width / dpi * 0.75;
|
||||
this.Top = screen.WorkingArea.Top + (int)(screen.WorkingArea.Height / dpi * 0.05);
|
||||
this.Left = screen.WorkingArea.Left + (int)(screen.WorkingArea.Width / dpi * 0.125);
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
_mainPage = new MainPage(mainViewModel);
|
||||
|
||||
ContentFrame.Navigate(_mainPage);
|
||||
|
||||
MaxWidth = SystemParameters.PrimaryScreenWidth;
|
||||
MaxHeight = SystemParameters.PrimaryScreenHeight;
|
||||
}
|
||||
|
||||
private void OnClosing(object sender, EventArgs e)
|
||||
{
|
||||
App.Current.Shutdown();
|
||||
}
|
||||
|
||||
// This is required to fix a WPF rendering bug when using custom chrome
|
||||
private void OnContentRendered(object sender, EventArgs e)
|
||||
{
|
||||
// Get the window handle of the Workspaces Editor window
|
||||
IntPtr handle = new WindowInteropHelper(this).Handle;
|
||||
WindowHelpers.BringToForeground(handle);
|
||||
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
public void ShowPage(ProjectEditor editPage)
|
||||
{
|
||||
ContentFrame.Navigate(editPage);
|
||||
}
|
||||
|
||||
public void SwitchToMainView()
|
||||
{
|
||||
ContentFrame.GoBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
namespace WorkspacesEditor.Models
|
||||
{
|
||||
public sealed class AppListDataTemplateSelector : System.Windows.Controls.DataTemplateSelector
|
||||
{
|
||||
public System.Windows.DataTemplate HeaderTemplate { get; set; }
|
||||
|
||||
public System.Windows.DataTemplate AppTemplate { get; set; }
|
||||
|
||||
public AppListDataTemplateSelector()
|
||||
{
|
||||
HeaderTemplate = new System.Windows.DataTemplate();
|
||||
AppTemplate = new System.Windows.DataTemplate();
|
||||
}
|
||||
|
||||
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
|
||||
{
|
||||
if (item is MonitorHeaderRow)
|
||||
{
|
||||
return HeaderTemplate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppTemplate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
474
src/modules/Workspaces/WorkspacesEditor/Models/Application.cs
Normal file
474
src/modules/Workspaces/WorkspacesEditor/Models/Application.cs
Normal file
@@ -0,0 +1,474 @@
|
||||
// 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 System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ManagedCommon;
|
||||
using Windows.Management.Deployment;
|
||||
|
||||
namespace WorkspacesEditor.Models
|
||||
{
|
||||
public class Application : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
private bool _isInitialized;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public Application()
|
||||
{
|
||||
}
|
||||
|
||||
public Application(Application other)
|
||||
{
|
||||
AppName = other.AppName;
|
||||
AppPath = other.AppPath;
|
||||
AppTitle = other.AppTitle;
|
||||
PackageFullName = other.PackageFullName;
|
||||
AppUserModelId = other.AppUserModelId;
|
||||
CommandLineArguments = other.CommandLineArguments;
|
||||
IsElevated = other.IsElevated;
|
||||
CanLaunchElevated = other.CanLaunchElevated;
|
||||
Minimized = other.Minimized;
|
||||
Maximized = other.Maximized;
|
||||
Position = other.Position;
|
||||
MonitorNumber = other.MonitorNumber;
|
||||
|
||||
Parent = other.Parent;
|
||||
IsNotFound = other.IsNotFound;
|
||||
IsHighlighted = other.IsHighlighted;
|
||||
RepeatIndex = other.RepeatIndex;
|
||||
PackagedId = other.PackagedId;
|
||||
PackagedName = other.PackagedName;
|
||||
PackagedPublisherID = other.PackagedPublisherID;
|
||||
Aumid = other.Aumid;
|
||||
IsExpanded = other.IsExpanded;
|
||||
IsIncluded = other.IsIncluded;
|
||||
}
|
||||
|
||||
public Project Parent { get; set; }
|
||||
|
||||
public struct WindowPosition
|
||||
{
|
||||
public int X { get; set; }
|
||||
|
||||
public int Y { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
public int Height { get; set; }
|
||||
|
||||
public static bool operator ==(WindowPosition left, WindowPosition right)
|
||||
{
|
||||
return left.X == right.X && left.Y == right.Y && left.Width == right.Width && left.Height == right.Height;
|
||||
}
|
||||
|
||||
public static bool operator !=(WindowPosition left, WindowPosition right)
|
||||
{
|
||||
return left.X != right.X || left.Y != right.Y || left.Width != right.Width || left.Height != right.Height;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || GetType() != obj.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WindowPosition pos = (WindowPosition)obj;
|
||||
return X == pos.X && Y == pos.Y && Width == pos.Width && Height == pos.Height;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public string AppName { get; set; }
|
||||
|
||||
public string AppPath { get; set; }
|
||||
|
||||
public string AppTitle { get; set; }
|
||||
|
||||
public string PackageFullName { get; set; }
|
||||
|
||||
public string AppUserModelId { get; set; }
|
||||
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
private bool _isElevated;
|
||||
|
||||
public bool IsElevated
|
||||
{
|
||||
get => _isElevated;
|
||||
set
|
||||
{
|
||||
_isElevated = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(AppMainParams)));
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanLaunchElevated { get; set; }
|
||||
|
||||
internal void SwitchDeletion()
|
||||
{
|
||||
IsIncluded = !IsIncluded;
|
||||
RedrawPreviewImage();
|
||||
}
|
||||
|
||||
private void RedrawPreviewImage()
|
||||
{
|
||||
if (_isInitialized)
|
||||
{
|
||||
Parent.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
}
|
||||
|
||||
private bool _minimized;
|
||||
|
||||
public bool Minimized
|
||||
{
|
||||
get => _minimized;
|
||||
set
|
||||
{
|
||||
_minimized = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Minimized)));
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(EditPositionEnabled)));
|
||||
RedrawPreviewImage();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _maximized;
|
||||
|
||||
public bool Maximized
|
||||
{
|
||||
get => _maximized;
|
||||
set
|
||||
{
|
||||
_maximized = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Maximized)));
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(EditPositionEnabled)));
|
||||
RedrawPreviewImage();
|
||||
}
|
||||
}
|
||||
|
||||
public bool EditPositionEnabled { get => !Minimized && !Maximized; }
|
||||
|
||||
private string _appMainParams;
|
||||
|
||||
public string AppMainParams
|
||||
{
|
||||
get
|
||||
{
|
||||
_appMainParams = _isElevated ? Properties.Resources.Admin : string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(CommandLineArguments))
|
||||
{
|
||||
_appMainParams += (_appMainParams == string.Empty ? string.Empty : " | ") + Properties.Resources.Args + ": " + CommandLineArguments;
|
||||
}
|
||||
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsAppMainParamVisible)));
|
||||
return _appMainParams;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAppMainParamVisible { get => !string.IsNullOrWhiteSpace(_appMainParams); }
|
||||
|
||||
private bool _isNotFound;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsNotFound
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isNotFound;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_isNotFound != value)
|
||||
{
|
||||
_isNotFound = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsNotFound)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsHighlighted { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public int RepeatIndex { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string RepeatIndexString
|
||||
{
|
||||
get
|
||||
{
|
||||
return RepeatIndex <= 1 ? string.Empty : RepeatIndex.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
private Icon _icon = null;
|
||||
|
||||
[JsonIgnore]
|
||||
public Icon Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_icon == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsPackagedApp)
|
||||
{
|
||||
Uri uri = GetAppLogoByPackageFamilyName();
|
||||
var bitmap = new Bitmap(uri.LocalPath);
|
||||
var iconHandle = bitmap.GetHicon();
|
||||
_icon = Icon.FromHandle(iconHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
_icon = Icon.ExtractAssociatedIcon(AppPath);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.LogWarning($"Icon not found on app path: {AppPath}. Using default icon");
|
||||
IsNotFound = true;
|
||||
_icon = new Icon(@"images\DefaultIcon.ico");
|
||||
}
|
||||
}
|
||||
|
||||
return _icon;
|
||||
}
|
||||
}
|
||||
|
||||
public Uri GetAppLogoByPackageFamilyName()
|
||||
{
|
||||
var pkgManager = new PackageManager();
|
||||
var pkg = pkgManager.FindPackagesForUser(string.Empty, PackagedId).FirstOrDefault();
|
||||
|
||||
if (pkg == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return pkg.Logo;
|
||||
}
|
||||
|
||||
private BitmapImage _iconBitmapImage;
|
||||
|
||||
public BitmapImage IconBitmapImage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_iconBitmapImage == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Bitmap previewBitmap = new Bitmap(32, 32);
|
||||
using (Graphics graphics = Graphics.FromImage(previewBitmap))
|
||||
{
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
graphics.DrawIcon(Icon, new Rectangle(0, 0, 32, 32));
|
||||
}
|
||||
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
previewBitmap.Save(memory, ImageFormat.Png);
|
||||
memory.Position = 0;
|
||||
|
||||
var bitmapImage = new BitmapImage();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = memory;
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
bitmapImage.Freeze();
|
||||
|
||||
_iconBitmapImage = bitmapImage;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Exception while drawing icon for app with path: {AppPath}. Exception message: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return _iconBitmapImage;
|
||||
}
|
||||
}
|
||||
|
||||
private WindowPosition _position;
|
||||
|
||||
public WindowPosition Position
|
||||
{
|
||||
get => _position;
|
||||
set
|
||||
{
|
||||
_position = value;
|
||||
_scaledPosition = null;
|
||||
}
|
||||
}
|
||||
|
||||
private WindowPosition? _scaledPosition;
|
||||
|
||||
public WindowPosition ScaledPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_scaledPosition == null)
|
||||
{
|
||||
double scaleFactor = MonitorSetup.Dpi / 96.0;
|
||||
_scaledPosition = new WindowPosition()
|
||||
{
|
||||
X = (int)(scaleFactor * Position.X),
|
||||
Y = (int)(scaleFactor * Position.Y),
|
||||
Height = (int)(scaleFactor * Position.Height),
|
||||
Width = (int)(scaleFactor * Position.Width),
|
||||
};
|
||||
}
|
||||
|
||||
return _scaledPosition.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MonitorNumber { get; set; }
|
||||
|
||||
private MonitorSetup _monitorSetup;
|
||||
|
||||
public MonitorSetup MonitorSetup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_monitorSetup == null)
|
||||
{
|
||||
_monitorSetup = Parent.Monitors.Where(x => x.MonitorNumber == MonitorNumber).FirstOrDefault();
|
||||
}
|
||||
|
||||
return _monitorSetup;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public void InitializationFinished()
|
||||
{
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
private bool? _isPackagedApp;
|
||||
|
||||
public string PackagedId { get; set; }
|
||||
|
||||
public string PackagedName { get; set; }
|
||||
|
||||
public string PackagedPublisherID { get; set; }
|
||||
|
||||
public string Aumid { get; set; }
|
||||
|
||||
public bool IsPackagedApp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isPackagedApp == null)
|
||||
{
|
||||
if (!AppPath.StartsWith("C:\\Program Files\\WindowsApps\\", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_isPackagedApp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
string appPath = AppPath.Replace("C:\\Program Files\\WindowsApps\\", string.Empty);
|
||||
Regex packagedAppPathRegex = new Regex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_x64__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
Match match = packagedAppPathRegex.Match(appPath);
|
||||
_isPackagedApp = match.Success;
|
||||
if (match.Success)
|
||||
{
|
||||
PackagedName = match.Groups["APPID"].Value;
|
||||
PackagedPublisherID = match.Groups["PublisherID"].Value;
|
||||
PackagedId = $"{PackagedName}_{PackagedPublisherID}";
|
||||
Aumid = $"{PackagedId}!App";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _isPackagedApp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isExpanded;
|
||||
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => _isExpanded;
|
||||
set
|
||||
{
|
||||
if (_isExpanded != value)
|
||||
{
|
||||
_isExpanded = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsExpanded)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string DeleteButtonContent { get => _isIncluded ? Properties.Resources.Delete : Properties.Resources.AddBack; }
|
||||
|
||||
private bool _isIncluded = true;
|
||||
|
||||
public bool IsIncluded
|
||||
{
|
||||
get => _isIncluded;
|
||||
set
|
||||
{
|
||||
if (_isIncluded != value)
|
||||
{
|
||||
_isIncluded = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsIncluded)));
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(DeleteButtonContent)));
|
||||
if (!_isIncluded)
|
||||
{
|
||||
IsExpanded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
internal void CommandLineTextChanged(string newCommandLineValue)
|
||||
{
|
||||
CommandLineArguments = newCommandLineValue;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(AppMainParams)));
|
||||
}
|
||||
|
||||
internal void MaximizedChecked()
|
||||
{
|
||||
Minimized = false;
|
||||
}
|
||||
|
||||
internal void MinimizedChecked()
|
||||
{
|
||||
Maximized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/modules/Workspaces/WorkspacesEditor/Models/Monitor.cs
Normal file
33
src/modules/Workspaces/WorkspacesEditor/Models/Monitor.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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 System.Windows;
|
||||
|
||||
namespace WorkspacesEditor.Models
|
||||
{
|
||||
public class Monitor
|
||||
{
|
||||
public string MonitorName { get; private set; }
|
||||
|
||||
public string MonitorInstanceId { get; private set; }
|
||||
|
||||
public int MonitorNumber { get; private set; }
|
||||
|
||||
public int Dpi { get; private set; }
|
||||
|
||||
public Rect MonitorDpiUnawareBounds { get; private set; }
|
||||
|
||||
public Rect MonitorDpiAwareBounds { get; private set; }
|
||||
|
||||
public Monitor(string monitorName, string monitorInstanceId, int number, int dpi, Rect dpiAwareBounds, Rect dpiUnawareBounds)
|
||||
{
|
||||
MonitorName = monitorName;
|
||||
MonitorInstanceId = monitorInstanceId;
|
||||
MonitorNumber = number;
|
||||
Dpi = dpi;
|
||||
MonitorDpiAwareBounds = dpiAwareBounds;
|
||||
MonitorDpiUnawareBounds = dpiUnawareBounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
namespace WorkspacesEditor.Models
|
||||
{
|
||||
internal sealed class MonitorHeaderRow
|
||||
{
|
||||
public string MonitorName { get; set; }
|
||||
|
||||
public string SelectString { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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 System.ComponentModel;
|
||||
using System.Windows;
|
||||
|
||||
namespace WorkspacesEditor.Models
|
||||
{
|
||||
public class MonitorSetup : Monitor, INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public string MonitorInfo { get => MonitorName; }
|
||||
|
||||
public string MonitorInfoWithResolution { get => $"{MonitorName} {MonitorDpiAwareBounds.Width}x{MonitorDpiAwareBounds.Height}"; }
|
||||
|
||||
public MonitorSetup(string monitorName, string monitorInstanceId, int number, int dpi, Rect dpiAwareBounds, Rect dpiUnawareBounds)
|
||||
: base(monitorName, monitorInstanceId, number, dpi, dpiAwareBounds, dpiUnawareBounds)
|
||||
{
|
||||
}
|
||||
|
||||
public MonitorSetup(MonitorSetup other)
|
||||
: base(other.MonitorName, other.MonitorInstanceId, other.MonitorNumber, other.Dpi, other.MonitorDpiAwareBounds, other.MonitorDpiUnawareBounds)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
376
src/modules/Workspaces/WorkspacesEditor/Models/Project.cs
Normal file
376
src/modules/Workspaces/WorkspacesEditor/Models/Project.cs
Normal file
@@ -0,0 +1,376 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ManagedCommon;
|
||||
using WorkspacesEditor.Data;
|
||||
using WorkspacesEditor.Utils;
|
||||
|
||||
namespace WorkspacesEditor.Models
|
||||
{
|
||||
public class Project : INotifyPropertyChanged
|
||||
{
|
||||
[JsonIgnore]
|
||||
public string EditorWindowTitle { get; set; }
|
||||
|
||||
public string Id { get; private set; }
|
||||
|
||||
private string _name;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Name)));
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(CanBeSaved)));
|
||||
}
|
||||
}
|
||||
|
||||
public long CreationTime { get; } // in seconds
|
||||
|
||||
public long LastLaunchedTime { get; } // in seconds
|
||||
|
||||
public bool IsShortcutNeeded { get; set; }
|
||||
|
||||
public bool MoveExistingWindows { get; set; }
|
||||
|
||||
public string LastLaunched
|
||||
{
|
||||
get
|
||||
{
|
||||
string lastLaunched = WorkspacesEditor.Properties.Resources.LastLaunched + ": ";
|
||||
if (LastLaunchedTime == 0)
|
||||
{
|
||||
return lastLaunched + WorkspacesEditor.Properties.Resources.Never;
|
||||
}
|
||||
|
||||
const int SECOND = 1;
|
||||
const int MINUTE = 60 * SECOND;
|
||||
const int HOUR = 60 * MINUTE;
|
||||
const int DAY = 24 * HOUR;
|
||||
const int MONTH = 30 * DAY;
|
||||
|
||||
DateTime lastLaunchDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(LastLaunchedTime);
|
||||
|
||||
var now = DateTime.UtcNow.Ticks;
|
||||
var ts = DateTime.UtcNow - lastLaunchDateTime;
|
||||
double delta = Math.Abs(ts.TotalSeconds);
|
||||
|
||||
if (delta < 1 * MINUTE)
|
||||
{
|
||||
return lastLaunched + WorkspacesEditor.Properties.Resources.Recently;
|
||||
}
|
||||
|
||||
if (delta < 2 * MINUTE)
|
||||
{
|
||||
return lastLaunched + WorkspacesEditor.Properties.Resources.OneMinuteAgo;
|
||||
}
|
||||
|
||||
if (delta < 45 * MINUTE)
|
||||
{
|
||||
return lastLaunched + ts.Minutes + " " + WorkspacesEditor.Properties.Resources.MinutesAgo;
|
||||
}
|
||||
|
||||
if (delta < 90 * MINUTE)
|
||||
{
|
||||
return lastLaunched + WorkspacesEditor.Properties.Resources.OneHourAgo;
|
||||
}
|
||||
|
||||
if (delta < 24 * HOUR)
|
||||
{
|
||||
return lastLaunched + ts.Hours + " " + WorkspacesEditor.Properties.Resources.HoursAgo;
|
||||
}
|
||||
|
||||
if (delta < 48 * HOUR)
|
||||
{
|
||||
return lastLaunched + WorkspacesEditor.Properties.Resources.Yesterday;
|
||||
}
|
||||
|
||||
if (delta < 30 * DAY)
|
||||
{
|
||||
return lastLaunched + ts.Days + " " + WorkspacesEditor.Properties.Resources.DaysAgo;
|
||||
}
|
||||
|
||||
if (delta < 12 * MONTH)
|
||||
{
|
||||
int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30));
|
||||
return lastLaunched + (months <= 1 ? WorkspacesEditor.Properties.Resources.OneMonthAgo : months + " " + WorkspacesEditor.Properties.Resources.MonthsAgo);
|
||||
}
|
||||
else
|
||||
{
|
||||
int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365));
|
||||
return lastLaunched + (years <= 1 ? WorkspacesEditor.Properties.Resources.OneYearAgo : years + " " + WorkspacesEditor.Properties.Resources.YearsAgo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanBeSaved
|
||||
{
|
||||
get => Name.Length > 0 && Applications.Count > 0;
|
||||
}
|
||||
|
||||
private bool _isRevertEnabled;
|
||||
|
||||
public bool IsRevertEnabled
|
||||
{
|
||||
get => _isRevertEnabled;
|
||||
set
|
||||
{
|
||||
if (_isRevertEnabled != value)
|
||||
{
|
||||
_isRevertEnabled = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRevertEnabled)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isPopupVisible;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsPopupVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isPopupVisible;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_isPopupVisible = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsPopupVisible)));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Application> Applications { get; set; }
|
||||
|
||||
public List<object> ApplicationsListed
|
||||
{
|
||||
get
|
||||
{
|
||||
List<object> applicationsListed = new List<object>();
|
||||
ILookup<MonitorSetup, Application> apps = Applications.Where(x => !x.Minimized).ToLookup(x => x.MonitorSetup);
|
||||
foreach (var appItem in apps.OrderBy(x => x.Key.MonitorDpiUnawareBounds.Left).ThenBy(x => x.Key.MonitorDpiUnawareBounds.Top))
|
||||
{
|
||||
MonitorHeaderRow headerRow = new MonitorHeaderRow { MonitorName = "Screen " + appItem.Key.MonitorNumber, SelectString = Properties.Resources.SelectAllAppsOnMonitor + " " + appItem.Key.MonitorInfo };
|
||||
applicationsListed.Add(headerRow);
|
||||
foreach (Application app in appItem)
|
||||
{
|
||||
applicationsListed.Add(app);
|
||||
}
|
||||
}
|
||||
|
||||
var minimizedApps = Applications.Where(x => x.Minimized);
|
||||
if (minimizedApps.Any())
|
||||
{
|
||||
MonitorHeaderRow headerRow = new MonitorHeaderRow { MonitorName = Properties.Resources.Minimized_Apps, SelectString = Properties.Resources.SelectAllMinimizedApps };
|
||||
applicationsListed.Add(headerRow);
|
||||
foreach (Application app in minimizedApps)
|
||||
{
|
||||
applicationsListed.Add(app);
|
||||
}
|
||||
}
|
||||
|
||||
return applicationsListed;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string AppsCountString
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = Applications.Count;
|
||||
return count.ToString(CultureInfo.InvariantCulture) + " " + (count == 1 ? Properties.Resources.App : Properties.Resources.Apps);
|
||||
}
|
||||
}
|
||||
|
||||
public List<MonitorSetup> Monitors { get; }
|
||||
|
||||
public bool IsPositionChangedManually { get; set; } // telemetry
|
||||
|
||||
private BitmapImage _previewIcons;
|
||||
private BitmapImage _previewImage;
|
||||
private double _previewImageWidth;
|
||||
|
||||
public Project(Project selectedProject)
|
||||
{
|
||||
Id = selectedProject.Id;
|
||||
Name = selectedProject.Name;
|
||||
PreviewIcons = selectedProject.PreviewIcons;
|
||||
PreviewImage = selectedProject.PreviewImage;
|
||||
IsShortcutNeeded = selectedProject.IsShortcutNeeded;
|
||||
MoveExistingWindows = selectedProject.MoveExistingWindows;
|
||||
|
||||
int screenIndex = 1;
|
||||
|
||||
Monitors = new List<MonitorSetup>();
|
||||
foreach (var item in selectedProject.Monitors.OrderBy(x => x.MonitorDpiAwareBounds.Left).ThenBy(x => x.MonitorDpiAwareBounds.Top))
|
||||
{
|
||||
Monitors.Add(item);
|
||||
screenIndex++;
|
||||
}
|
||||
|
||||
Applications = new List<Application>();
|
||||
foreach (var item in selectedProject.Applications)
|
||||
{
|
||||
Application newApp = new Application(item);
|
||||
newApp.Parent = this;
|
||||
newApp.InitializationFinished();
|
||||
Applications.Add(newApp);
|
||||
}
|
||||
}
|
||||
|
||||
public Project(ProjectData.ProjectWrapper project)
|
||||
{
|
||||
Id = project.Id;
|
||||
Name = project.Name;
|
||||
CreationTime = project.CreationTime;
|
||||
LastLaunchedTime = project.LastLaunchedTime;
|
||||
IsShortcutNeeded = project.IsShortcutNeeded;
|
||||
MoveExistingWindows = project.MoveExistingWindows;
|
||||
Monitors = new List<MonitorSetup>() { };
|
||||
Applications = new List<Models.Application> { };
|
||||
|
||||
foreach (var app in project.Applications)
|
||||
{
|
||||
Models.Application newApp = new Models.Application()
|
||||
{
|
||||
AppName = app.Application,
|
||||
AppPath = app.ApplicationPath,
|
||||
AppTitle = app.Title,
|
||||
PackageFullName = app.PackageFullName,
|
||||
AppUserModelId = app.AppUserModelId,
|
||||
Parent = this,
|
||||
CommandLineArguments = app.CommandLineArguments,
|
||||
IsElevated = app.IsElevated,
|
||||
CanLaunchElevated = app.CanLaunchElevated,
|
||||
Maximized = app.Maximized,
|
||||
Minimized = app.Minimized,
|
||||
IsNotFound = false,
|
||||
Position = new Models.Application.WindowPosition()
|
||||
{
|
||||
Height = app.Position.Height,
|
||||
Width = app.Position.Width,
|
||||
X = app.Position.X,
|
||||
Y = app.Position.Y,
|
||||
},
|
||||
MonitorNumber = app.Monitor,
|
||||
};
|
||||
newApp.InitializationFinished();
|
||||
Applications.Add(newApp);
|
||||
}
|
||||
|
||||
foreach (var monitor in project.MonitorConfiguration)
|
||||
{
|
||||
System.Windows.Rect dpiAware = new System.Windows.Rect(monitor.MonitorRectDpiAware.Left, monitor.MonitorRectDpiAware.Top, monitor.MonitorRectDpiAware.Width, monitor.MonitorRectDpiAware.Height);
|
||||
System.Windows.Rect dpiUnaware = new System.Windows.Rect(monitor.MonitorRectDpiUnaware.Left, monitor.MonitorRectDpiUnaware.Top, monitor.MonitorRectDpiUnaware.Width, monitor.MonitorRectDpiUnaware.Height);
|
||||
Monitors.Add(new MonitorSetup(monitor.Id, monitor.InstanceId, monitor.MonitorNumber, monitor.Dpi, dpiAware, dpiUnaware));
|
||||
}
|
||||
}
|
||||
|
||||
public BitmapImage PreviewIcons
|
||||
{
|
||||
get
|
||||
{
|
||||
return _previewIcons;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_previewIcons = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(PreviewIcons)));
|
||||
}
|
||||
}
|
||||
|
||||
public BitmapImage PreviewImage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _previewImage;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_previewImage = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(PreviewImage)));
|
||||
}
|
||||
}
|
||||
|
||||
public double PreviewImageWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
return _previewImageWidth;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_previewImageWidth = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(PreviewImageWidth)));
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public async void Initialize(Theme currentTheme)
|
||||
{
|
||||
PreviewIcons = await Task.Run(() => DrawHelper.DrawPreviewIcons(this));
|
||||
Rectangle commonBounds = GetCommonBounds();
|
||||
PreviewImage = await Task.Run(() => DrawHelper.DrawPreview(this, commonBounds, currentTheme));
|
||||
PreviewImageWidth = commonBounds.Width / (commonBounds.Height * 1.2 / 200);
|
||||
}
|
||||
|
||||
private Rectangle GetCommonBounds()
|
||||
{
|
||||
double minX = Monitors.First().MonitorDpiAwareBounds.Left;
|
||||
double minY = Monitors.First().MonitorDpiAwareBounds.Top;
|
||||
double maxX = Monitors.First().MonitorDpiAwareBounds.Right;
|
||||
double maxY = Monitors.First().MonitorDpiAwareBounds.Bottom;
|
||||
for (int monitorIndex = 1; monitorIndex < Monitors.Count; monitorIndex++)
|
||||
{
|
||||
Monitor monitor = Monitors[monitorIndex];
|
||||
minX = Math.Min(minX, monitor.MonitorDpiAwareBounds.Left);
|
||||
minY = Math.Min(minY, monitor.MonitorDpiAwareBounds.Top);
|
||||
maxX = Math.Max(maxX, monitor.MonitorDpiAwareBounds.Right);
|
||||
maxY = Math.Max(maxY, monitor.MonitorDpiAwareBounds.Bottom);
|
||||
}
|
||||
|
||||
return new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
|
||||
}
|
||||
|
||||
public void UpdateAfterLaunchAndEdit(Project other)
|
||||
{
|
||||
Id = other.Id;
|
||||
Name = other.Name;
|
||||
IsRevertEnabled = true;
|
||||
}
|
||||
|
||||
internal void CloseExpanders()
|
||||
{
|
||||
foreach (Application app in Applications)
|
||||
{
|
||||
app.IsExpanded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/modules/Workspaces/WorkspacesEditor/OverlayWindow.xaml
Normal file
16
src/modules/Workspaces/WorkspacesEditor/OverlayWindow.xaml
Normal file
@@ -0,0 +1,16 @@
|
||||
<Window
|
||||
x:Class="WorkspacesEditor.OverlayWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:WorkspacesEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
AllowsTransparency="True"
|
||||
Background="Transparent"
|
||||
WindowStyle="None"
|
||||
mc:Ignorable="d">
|
||||
<Border
|
||||
Background="Transparent"
|
||||
BorderBrush="Red"
|
||||
BorderThickness="3" />
|
||||
</Window>
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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 System.Windows;
|
||||
|
||||
namespace WorkspacesEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for OverlayWindow.xaml
|
||||
/// </summary>
|
||||
public partial class OverlayWindow : Window
|
||||
{
|
||||
public OverlayWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
693
src/modules/Workspaces/WorkspacesEditor/Properties/Resources.Designer.cs
generated
Normal file
693
src/modules/Workspaces/WorkspacesEditor/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,693 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace WorkspacesEditor.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WorkspacesEditor.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Add Back.
|
||||
/// </summary>
|
||||
public static string AddBack {
|
||||
get {
|
||||
return ResourceManager.GetString("AddBack", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Admin.
|
||||
/// </summary>
|
||||
public static string Admin {
|
||||
get {
|
||||
return ResourceManager.GetString("Admin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch new app instances.
|
||||
/// </summary>
|
||||
public static string AlwaysLaunch {
|
||||
get {
|
||||
return ResourceManager.GetString("AlwaysLaunch", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to app.
|
||||
/// </summary>
|
||||
public static string App {
|
||||
get {
|
||||
return ResourceManager.GetString("App", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to App name.
|
||||
/// </summary>
|
||||
public static string App_name {
|
||||
get {
|
||||
return ResourceManager.GetString("App_name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to apps.
|
||||
/// </summary>
|
||||
public static string Apps {
|
||||
get {
|
||||
return ResourceManager.GetString("Apps", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure?.
|
||||
/// </summary>
|
||||
public static string Are_You_Sure {
|
||||
get {
|
||||
return ResourceManager.GetString("Are_You_Sure", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Are you sure you want to delete this Workspace?.
|
||||
/// </summary>
|
||||
public static string Are_You_Sure_Description {
|
||||
get {
|
||||
return ResourceManager.GetString("Are_You_Sure_Description", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Args.
|
||||
/// </summary>
|
||||
public static string Args {
|
||||
get {
|
||||
return ResourceManager.GetString("Args", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel.
|
||||
/// </summary>
|
||||
public static string Cancel {
|
||||
get {
|
||||
return ResourceManager.GetString("Cancel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CLI arguments.
|
||||
/// </summary>
|
||||
public static string CliArguments {
|
||||
get {
|
||||
return ResourceManager.GetString("CliArguments", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Created.
|
||||
/// </summary>
|
||||
public static string Created {
|
||||
get {
|
||||
return ResourceManager.GetString("Created", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Create Desktop Shortcut.
|
||||
/// </summary>
|
||||
public static string CreateShortcut {
|
||||
get {
|
||||
return ResourceManager.GetString("CreateShortcut", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Create Workspace.
|
||||
/// </summary>
|
||||
public static string CreateWorkspace {
|
||||
get {
|
||||
return ResourceManager.GetString("CreateWorkspace", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to days ago.
|
||||
/// </summary>
|
||||
public static string DaysAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("DaysAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Workspace.
|
||||
/// </summary>
|
||||
public static string DefaultWorkspaceNamePrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("DefaultWorkspaceNamePrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Remove.
|
||||
/// </summary>
|
||||
public static string Delete {
|
||||
get {
|
||||
return ResourceManager.GetString("Delete", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Delete Workspace dialog..
|
||||
/// </summary>
|
||||
public static string Delete_Workspace_Dialog_Announce {
|
||||
get {
|
||||
return ResourceManager.GetString("Delete_Workspace_Dialog_Announce", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Remove Selected Apps.
|
||||
/// </summary>
|
||||
public static string DeleteSelected {
|
||||
get {
|
||||
return ResourceManager.GetString("DeleteSelected", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit.
|
||||
/// </summary>
|
||||
public static string Edit {
|
||||
get {
|
||||
return ResourceManager.GetString("Edit", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to opened.
|
||||
/// </summary>
|
||||
public static string Edit_Project_Open_Announce {
|
||||
get {
|
||||
return ResourceManager.GetString("Edit_Project_Open_Announce", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit Workspace.
|
||||
/// </summary>
|
||||
public static string EditWorkspace {
|
||||
get {
|
||||
return ResourceManager.GetString("EditWorkspace", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error parsing Workspaces data..
|
||||
/// </summary>
|
||||
public static string Error_Parsing_Message {
|
||||
get {
|
||||
return ResourceManager.GetString("Error_Parsing_Message", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Height.
|
||||
/// </summary>
|
||||
public static string Height {
|
||||
get {
|
||||
return ResourceManager.GetString("Height", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to hours ago.
|
||||
/// </summary>
|
||||
public static string HoursAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("HoursAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Last launched.
|
||||
/// </summary>
|
||||
public static string LastLaunched {
|
||||
get {
|
||||
return ResourceManager.GetString("LastLaunched", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch.
|
||||
/// </summary>
|
||||
public static string Launch {
|
||||
get {
|
||||
return ResourceManager.GetString("Launch", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch args.
|
||||
/// </summary>
|
||||
public static string Launch_args {
|
||||
get {
|
||||
return ResourceManager.GetString("Launch_args", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch as Admin.
|
||||
/// </summary>
|
||||
public static string LaunchAsAdmin {
|
||||
get {
|
||||
return ResourceManager.GetString("LaunchAsAdmin", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch & Edit.
|
||||
/// </summary>
|
||||
public static string LaunchEdit {
|
||||
get {
|
||||
return ResourceManager.GetString("LaunchEdit", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Left.
|
||||
/// </summary>
|
||||
public static string Left {
|
||||
get {
|
||||
return ResourceManager.GetString("Left", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Workspaces Editor.
|
||||
/// </summary>
|
||||
public static string MainTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("MainTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Maximized.
|
||||
/// </summary>
|
||||
public static string Maximized {
|
||||
get {
|
||||
return ResourceManager.GetString("Maximized", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Minimized.
|
||||
/// </summary>
|
||||
public static string Minimized {
|
||||
get {
|
||||
return ResourceManager.GetString("Minimized", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Minimized Apps.
|
||||
/// </summary>
|
||||
public static string Minimized_Apps {
|
||||
get {
|
||||
return ResourceManager.GetString("Minimized_Apps", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to minutes ago.
|
||||
/// </summary>
|
||||
public static string MinutesAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("MinutesAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to months ago.
|
||||
/// </summary>
|
||||
public static string MonthsAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("MonthsAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Move apps if present.
|
||||
/// </summary>
|
||||
public static string MoveIfExist {
|
||||
get {
|
||||
return ResourceManager.GetString("MoveIfExist", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name.
|
||||
/// </summary>
|
||||
public static string Name {
|
||||
get {
|
||||
return ResourceManager.GetString("Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to never.
|
||||
/// </summary>
|
||||
public static string Never {
|
||||
get {
|
||||
return ResourceManager.GetString("Never", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to New Workspace.
|
||||
/// </summary>
|
||||
public static string New_Workspace {
|
||||
get {
|
||||
return ResourceManager.GetString("New_Workspace", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to There are no saved Workspaces..
|
||||
/// </summary>
|
||||
public static string No_Workspaces_Message {
|
||||
get {
|
||||
return ResourceManager.GetString("No_Workspaces_Message", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The application cannot be found.
|
||||
/// </summary>
|
||||
public static string NotFoundTooltip {
|
||||
get {
|
||||
return ResourceManager.GetString("NotFoundTooltip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No Workspaces match the current search..
|
||||
/// </summary>
|
||||
public static string NoWorkspacesMatch {
|
||||
get {
|
||||
return ResourceManager.GetString("NoWorkspacesMatch", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to an hour ago.
|
||||
/// </summary>
|
||||
public static string OneHourAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("OneHourAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to a minute ago.
|
||||
/// </summary>
|
||||
public static string OneMinuteAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("OneMinuteAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to one month ago.
|
||||
/// </summary>
|
||||
public static string OneMonthAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("OneMonthAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to one second ago.
|
||||
/// </summary>
|
||||
public static string OneSecondAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("OneSecondAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to one year ago.
|
||||
/// </summary>
|
||||
public static string OneYearAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("OneYearAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Pin Workspaces to Taskbar.
|
||||
/// </summary>
|
||||
public static string PinToTaskbar {
|
||||
get {
|
||||
return ResourceManager.GetString("PinToTaskbar", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to recently.
|
||||
/// </summary>
|
||||
public static string Recently {
|
||||
get {
|
||||
return ResourceManager.GetString("Recently", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Revert.
|
||||
/// </summary>
|
||||
public static string Revert {
|
||||
get {
|
||||
return ResourceManager.GetString("Revert", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Save Workspace.
|
||||
/// </summary>
|
||||
public static string Save_Workspace {
|
||||
get {
|
||||
return ResourceManager.GetString("Save_Workspace", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Search.
|
||||
/// </summary>
|
||||
public static string Search {
|
||||
get {
|
||||
return ResourceManager.GetString("Search", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Search for Workspaces or apps.
|
||||
/// </summary>
|
||||
public static string SearchExplanation {
|
||||
get {
|
||||
return ResourceManager.GetString("SearchExplanation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to seconds ago.
|
||||
/// </summary>
|
||||
public static string SecondsAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("SecondsAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Select All Apps on.
|
||||
/// </summary>
|
||||
public static string SelectAllAppsOnMonitor {
|
||||
get {
|
||||
return ResourceManager.GetString("SelectAllAppsOnMonitor", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Select All Minimized Apps.
|
||||
/// </summary>
|
||||
public static string SelectAllMinimizedApps {
|
||||
get {
|
||||
return ResourceManager.GetString("SelectAllMinimizedApps", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Select All Apps in Workspace.
|
||||
/// </summary>
|
||||
public static string SelectedAllInWorkspace {
|
||||
get {
|
||||
return ResourceManager.GetString("SelectedAllInWorkspace", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Edit your layout and click "Capture" when finished..
|
||||
/// </summary>
|
||||
public static string SnapshotDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("SnapshotDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Snapshot Creator.
|
||||
/// </summary>
|
||||
public static string SnapshotWindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("SnapshotWindowTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Sort by.
|
||||
/// </summary>
|
||||
public static string SortBy {
|
||||
get {
|
||||
return ResourceManager.GetString("SortBy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Capture.
|
||||
/// </summary>
|
||||
public static string Take_Snapshot {
|
||||
get {
|
||||
return ResourceManager.GetString("Take_Snapshot", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Top.
|
||||
/// </summary>
|
||||
public static string Top {
|
||||
get {
|
||||
return ResourceManager.GetString("Top", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Width.
|
||||
/// </summary>
|
||||
public static string Width {
|
||||
get {
|
||||
return ResourceManager.GetString("Width", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Workspace name.
|
||||
/// </summary>
|
||||
public static string WorkspaceName {
|
||||
get {
|
||||
return ResourceManager.GetString("WorkspaceName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Workspaces.
|
||||
/// </summary>
|
||||
public static string Workspaces {
|
||||
get {
|
||||
return ResourceManager.GetString("Workspaces", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Write arguments here.
|
||||
/// </summary>
|
||||
public static string WriteArgs {
|
||||
get {
|
||||
return ResourceManager.GetString("WriteArgs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to years ago.
|
||||
/// </summary>
|
||||
public static string YearsAgo {
|
||||
get {
|
||||
return ResourceManager.GetString("YearsAgo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to yesterday.
|
||||
/// </summary>
|
||||
public static string Yesterday {
|
||||
get {
|
||||
return ResourceManager.GetString("Yesterday", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AddBack" xml:space="preserve">
|
||||
<value>Add back</value>
|
||||
</data>
|
||||
<data name="Admin" xml:space="preserve">
|
||||
<value>Admin</value>
|
||||
</data>
|
||||
<data name="AlwaysLaunch" xml:space="preserve">
|
||||
<value>Launch new app instances</value>
|
||||
</data>
|
||||
<data name="App" xml:space="preserve">
|
||||
<value>app</value>
|
||||
</data>
|
||||
<data name="Apps" xml:space="preserve">
|
||||
<value>apps</value>
|
||||
</data>
|
||||
<data name="App_name" xml:space="preserve">
|
||||
<value>App name</value>
|
||||
</data>
|
||||
<data name="Are_You_Sure" xml:space="preserve">
|
||||
<value>Are you sure?</value>
|
||||
</data>
|
||||
<data name="Are_You_Sure_Description" xml:space="preserve">
|
||||
<value>Are you sure you want to delete this Workspace?</value>
|
||||
</data>
|
||||
<data name="Args" xml:space="preserve">
|
||||
<value>Args</value>
|
||||
<comment>Arguments</comment>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="CliArguments" xml:space="preserve">
|
||||
<value>CLI arguments</value>
|
||||
</data>
|
||||
<data name="Created" xml:space="preserve">
|
||||
<value>Created</value>
|
||||
</data>
|
||||
<data name="CreateWorkspace" xml:space="preserve">
|
||||
<value>Create Workspace</value>
|
||||
</data>
|
||||
<data name="CreateShortcut" xml:space="preserve">
|
||||
<value>Create desktop shortcut</value>
|
||||
</data>
|
||||
<data name="DaysAgo" xml:space="preserve">
|
||||
<value>days ago</value>
|
||||
</data>
|
||||
<data name="DefaultWorkspaceNamePrefix" xml:space="preserve">
|
||||
<value>Workspace</value>
|
||||
</data>
|
||||
<data name="Delete" xml:space="preserve">
|
||||
<value>Remove</value>
|
||||
</data>
|
||||
<data name="DeleteSelected" xml:space="preserve">
|
||||
<value>Remove selected apps</value>
|
||||
</data>
|
||||
<data name="Delete_Workspace_Dialog_Announce" xml:space="preserve">
|
||||
<value>Delete Workspace dialog.</value>
|
||||
</data>
|
||||
<data name="Edit" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
</data>
|
||||
<data name="EditWorkspace" xml:space="preserve">
|
||||
<value>Edit Workspace</value>
|
||||
</data>
|
||||
<data name="Edit_Project_Open_Announce" xml:space="preserve">
|
||||
<value>opened</value>
|
||||
</data>
|
||||
<data name="Error_Parsing_Message" xml:space="preserve">
|
||||
<value>Error parsing Workspaces data.</value>
|
||||
</data>
|
||||
<data name="Height" xml:space="preserve">
|
||||
<value>Height</value>
|
||||
</data>
|
||||
<data name="HoursAgo" xml:space="preserve">
|
||||
<value>hours ago</value>
|
||||
</data>
|
||||
<data name="LastLaunched" xml:space="preserve">
|
||||
<value>Last launched</value>
|
||||
</data>
|
||||
<data name="Launch" xml:space="preserve">
|
||||
<value>Launch</value>
|
||||
</data>
|
||||
<data name="LaunchAsAdmin" xml:space="preserve">
|
||||
<value>Launch as Admin</value>
|
||||
</data>
|
||||
<data name="LaunchEdit" xml:space="preserve">
|
||||
<value>Launch & edit</value>
|
||||
</data>
|
||||
<data name="Launch_args" xml:space="preserve">
|
||||
<value>Launch args</value>
|
||||
</data>
|
||||
<data name="Left" xml:space="preserve">
|
||||
<value>Left</value>
|
||||
<comment>the left x coordinate</comment>
|
||||
</data>
|
||||
<data name="MainTitle" xml:space="preserve">
|
||||
<value>Workspaces Editor</value>
|
||||
</data>
|
||||
<data name="Maximized" xml:space="preserve">
|
||||
<value>Maximized</value>
|
||||
</data>
|
||||
<data name="Minimized" xml:space="preserve">
|
||||
<value>Minimized</value>
|
||||
</data>
|
||||
<data name="Minimized_Apps" xml:space="preserve">
|
||||
<value>Minimized apps</value>
|
||||
</data>
|
||||
<data name="MinutesAgo" xml:space="preserve">
|
||||
<value>minutes ago</value>
|
||||
</data>
|
||||
<data name="MonthsAgo" xml:space="preserve">
|
||||
<value>months ago</value>
|
||||
</data>
|
||||
<data name="MoveIfExist" xml:space="preserve">
|
||||
<value>Move apps if present</value>
|
||||
</data>
|
||||
<data name="Name" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="Never" xml:space="preserve">
|
||||
<value>never</value>
|
||||
</data>
|
||||
<data name="New_Workspace" xml:space="preserve">
|
||||
<value>New Workspace</value>
|
||||
</data>
|
||||
<data name="NoWorkspacesMatch" xml:space="preserve">
|
||||
<value>No Workspaces match the current search.</value>
|
||||
</data>
|
||||
<data name="NotFoundTooltip" xml:space="preserve">
|
||||
<value>The application cannot be found</value>
|
||||
</data>
|
||||
<data name="No_Workspaces_Message" xml:space="preserve">
|
||||
<value>There are no saved Workspaces.</value>
|
||||
</data>
|
||||
<data name="OneHourAgo" xml:space="preserve">
|
||||
<value>an hour ago</value>
|
||||
</data>
|
||||
<data name="OneMinuteAgo" xml:space="preserve">
|
||||
<value>a minute ago</value>
|
||||
</data>
|
||||
<data name="OneMonthAgo" xml:space="preserve">
|
||||
<value>one month ago</value>
|
||||
</data>
|
||||
<data name="OneSecondAgo" xml:space="preserve">
|
||||
<value>one second ago</value>
|
||||
</data>
|
||||
<data name="OneYearAgo" xml:space="preserve">
|
||||
<value>one year ago</value>
|
||||
</data>
|
||||
<data name="PinToTaskbar" xml:space="preserve">
|
||||
<value>Pin Workspaces to taskbar</value>
|
||||
</data>
|
||||
<data name="WorkspaceName" xml:space="preserve">
|
||||
<value>Workspace name</value>
|
||||
</data>
|
||||
<data name="Workspaces" xml:space="preserve">
|
||||
<value>Workspaces</value>
|
||||
</data>
|
||||
<data name="Recently" xml:space="preserve">
|
||||
<value>recently</value>
|
||||
</data>
|
||||
<data name="Revert" xml:space="preserve">
|
||||
<value>Revert</value>
|
||||
</data>
|
||||
<data name="Save_Workspace" xml:space="preserve">
|
||||
<value>Save Workspace</value>
|
||||
</data>
|
||||
<data name="Search" xml:space="preserve">
|
||||
<value>Search</value>
|
||||
</data>
|
||||
<data name="SearchExplanation" xml:space="preserve">
|
||||
<value>Search for Workspaces or apps</value>
|
||||
</data>
|
||||
<data name="SecondsAgo" xml:space="preserve">
|
||||
<value>seconds ago</value>
|
||||
</data>
|
||||
<data name="SelectAllAppsOnMonitor" xml:space="preserve">
|
||||
<value>Select all apps on</value>
|
||||
</data>
|
||||
<data name="SelectAllMinimizedApps" xml:space="preserve">
|
||||
<value>Select all minimized apps</value>
|
||||
</data>
|
||||
<data name="SelectedAllInWorkspace" xml:space="preserve">
|
||||
<value>Select all apps in Workspace</value>
|
||||
</data>
|
||||
<data name="SnapshotDescription" xml:space="preserve">
|
||||
<value>Edit your layout and click "Capture" when finished.</value>
|
||||
</data>
|
||||
<data name="SnapshotWindowTitle" xml:space="preserve">
|
||||
<value>Snapshot Creator</value>
|
||||
</data>
|
||||
<data name="SortBy" xml:space="preserve">
|
||||
<value>Sort by</value>
|
||||
</data>
|
||||
<data name="Take_Snapshot" xml:space="preserve">
|
||||
<value>Capture</value>
|
||||
</data>
|
||||
<data name="Top" xml:space="preserve">
|
||||
<value>Top</value>
|
||||
<comment>the top y coordinate</comment>
|
||||
</data>
|
||||
<data name="Width" xml:space="preserve">
|
||||
<value>Width</value>
|
||||
</data>
|
||||
<data name="WriteArgs" xml:space="preserve">
|
||||
<value>Write arguments here</value>
|
||||
</data>
|
||||
<data name="YearsAgo" xml:space="preserve">
|
||||
<value>years ago</value>
|
||||
</data>
|
||||
<data name="Yesterday" xml:space="preserve">
|
||||
<value>yesterday</value>
|
||||
</data>
|
||||
</root>
|
||||
26
src/modules/Workspaces/WorkspacesEditor/Properties/Settings.Designer.cs
generated
Normal file
26
src/modules/Workspaces/WorkspacesEditor/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace WorkspacesEditor.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
59
src/modules/Workspaces/WorkspacesEditor/SnapshotWindow.xaml
Normal file
59
src/modules/Workspaces/WorkspacesEditor/SnapshotWindow.xaml
Normal file
@@ -0,0 +1,59 @@
|
||||
<Window
|
||||
x:Class="WorkspacesEditor.SnapshotWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:WorkspacesEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:props="clr-namespace:WorkspacesEditor.Properties"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
Title="{x:Static props:Resources.SnapshotWindowTitle}"
|
||||
Width="350"
|
||||
Height="140"
|
||||
ui:TitleBar.Background="{DynamicResource PrimaryBackgroundBrush}"
|
||||
ui:TitleBar.InactiveBackground="{DynamicResource TertiaryBackgroundBrush}"
|
||||
ui:WindowHelper.UseModernWindowStyle="True"
|
||||
Background="{DynamicResource PrimaryBackgroundBrush}"
|
||||
BorderBrush="Red"
|
||||
BorderThickness="5"
|
||||
Closing="Window_Closing"
|
||||
ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d">
|
||||
<Grid Margin="5" Background="Transparent">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="5"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.SnapshotDescription}" />
|
||||
<Button
|
||||
x:Name="CancelButton"
|
||||
Grid.Row="1"
|
||||
Height="36"
|
||||
Margin="5,5,5,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Cancel}"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
Click="CancelButtonClicked"
|
||||
Content="{x:Static props:Resources.Cancel}" />
|
||||
<Button
|
||||
x:Name="SnapshotButton"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Height="36"
|
||||
Margin="5,5,5,5"
|
||||
HorizontalAlignment="Stretch"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Take_Snapshot}"
|
||||
Click="SnapshotButtonClicked"
|
||||
Content="{x:Static props:Resources.Take_Snapshot}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 System.Windows;
|
||||
using WorkspacesEditor.ViewModels;
|
||||
|
||||
namespace WorkspacesEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for SnapshotWindow.xaml
|
||||
/// </summary>
|
||||
public partial class SnapshotWindow : Window
|
||||
{
|
||||
private MainViewModel _mainViewModel;
|
||||
|
||||
public SnapshotWindow(MainViewModel mainViewModel)
|
||||
{
|
||||
_mainViewModel = mainViewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void CancelButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
_mainViewModel.CancelSnapshot();
|
||||
}
|
||||
|
||||
private void SnapshotButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
_mainViewModel.SnapWorkspace();
|
||||
}
|
||||
|
||||
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
_mainViewModel.CancelSnapshot();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019">
|
||||
|
||||
<Style
|
||||
x:Key="IconOnlyButtonStyle"
|
||||
BasedOn="{StaticResource DefaultButtonStyle}"
|
||||
TargetType="Button">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Background"
|
||||
Background="Transparent"
|
||||
CornerRadius="{TemplateBinding ui:ControlHelper.CornerRadius}"
|
||||
SnapsToDevicePixels="True">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding ui:ControlHelper.CornerRadius}">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Focusable="False"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Background" Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="Background" Property="Background" Value="{DynamicResource ButtonBackgroundPressed}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="Background" Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
|
||||
<Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace WorkspacesEditor.Telemetry
|
||||
{
|
||||
[EventData]
|
||||
public class CreateEvent : EventBase, IEvent
|
||||
{
|
||||
public CreateEvent()
|
||||
{
|
||||
EventName = "Workspaces_CreateEvent";
|
||||
}
|
||||
|
||||
// True if operation successfully completely. False if failed
|
||||
public bool Successful { get; set; }
|
||||
|
||||
// Number of screens present in the project
|
||||
public int NumScreens { get; set; }
|
||||
|
||||
// Total number of apps in the project
|
||||
public int AppCount { get; set; }
|
||||
|
||||
// Number of apps with CLI args
|
||||
public int CliCount { get; set; }
|
||||
|
||||
// Number of apps with "Launch as admin" set
|
||||
public int AdminCount { get; set; }
|
||||
|
||||
// True of user checked "Create Shortcut". False if not.
|
||||
public bool ShortcutCreated { get; set; }
|
||||
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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 System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace WorkspacesEditor.Telemetry
|
||||
{
|
||||
[EventData]
|
||||
public class DeleteEvent : EventBase, IEvent
|
||||
{
|
||||
public DeleteEvent()
|
||||
{
|
||||
EventName = "Workspaces_DeleteEvent";
|
||||
}
|
||||
|
||||
public bool Successful { get; set; }
|
||||
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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 System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace WorkspacesEditor.Telemetry
|
||||
{
|
||||
[EventData]
|
||||
public class EditEvent : EventBase, IEvent
|
||||
{
|
||||
public EditEvent()
|
||||
{
|
||||
EventName = "Workspaces_EditEvent";
|
||||
}
|
||||
|
||||
// True if operation successfully completely. False if failed.
|
||||
public bool Successful { get; set; }
|
||||
|
||||
// Change in number of screens in project
|
||||
public int ScreenCountDelta { get; set; }
|
||||
|
||||
// Number of apps added to project through editing
|
||||
public int AppsAdded { get; set; }
|
||||
|
||||
// Number of apps removed from project through editing
|
||||
public int AppsRemoved { get; set; }
|
||||
|
||||
// Number of apps with CLI args added
|
||||
public int CliAdded { get; set; }
|
||||
|
||||
// Number of apps with CLI args removed
|
||||
public int CliRemoved { get; set; }
|
||||
|
||||
// Number of apps with admin added
|
||||
public int AdminAdded { get; set; }
|
||||
|
||||
// Number of apps with admin removed
|
||||
public int AdminRemoved { get; set; }
|
||||
|
||||
// True if used window pixel sizing boxes to adjust size
|
||||
public bool PixelAdjustmentsUsed { get; set; }
|
||||
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
}
|
||||
}
|
||||
26
src/modules/Workspaces/WorkspacesEditor/Themes/Dark.xaml
Normal file
26
src/modules/Workspaces/WorkspacesEditor/Themes/Dark.xaml
Normal file
@@ -0,0 +1,26 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<!-- Metadata -->
|
||||
<system:String x:Key="Theme.Name">Dark.Accent1</system:String>
|
||||
<system:String x:Key="Theme.Origin">Origin</system:String>
|
||||
<system:String x:Key="Theme.DisplayName">Accent1 (Dark)</system:String>
|
||||
<system:String x:Key="Theme.BaseColorScheme">Dark</system:String>
|
||||
<system:String x:Key="Theme.ColorScheme">Accent1</system:String>
|
||||
<Color x:Key="Theme.PrimaryAccentColor">Black</Color>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
|
||||
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF2b2b2b" />
|
||||
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF373737" />
|
||||
<SolidColorBrush x:Key="QuaternaryBackgroundBrush" Color="#FF272727" />
|
||||
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
|
||||
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFFFFFFF" />
|
||||
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF9a9a9a" />
|
||||
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
|
||||
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
|
||||
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
|
||||
<SolidColorBrush x:Key="BackdropBrush" Color="#40F0F0F0" />
|
||||
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,26 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<!-- Metadata -->
|
||||
<system:String x:Key="Theme.Name">HighContrast.Accent2</system:String>
|
||||
<system:String x:Key="Theme.Origin">Origin</system:String>
|
||||
<system:String x:Key="Theme.DisplayName">Accent2 (HighContrast)</system:String>
|
||||
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
|
||||
<system:String x:Key="Theme.ColorScheme">Accent2</system:String>
|
||||
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
|
||||
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
|
||||
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
|
||||
<SolidColorBrush x:Key="QuaternaryBackgroundBrush" Color="#FF272727" />
|
||||
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
|
||||
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFffff00" />
|
||||
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF00ff00" />
|
||||
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
|
||||
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
|
||||
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
|
||||
<SolidColorBrush x:Key="BackdropBrush" Color="#E55B5B5B" />
|
||||
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,26 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<!-- Metadata -->
|
||||
<system:String x:Key="Theme.Name">HighContrast.Accent3</system:String>
|
||||
<system:String x:Key="Theme.Origin">Origin</system:String>
|
||||
<system:String x:Key="Theme.DisplayName">Accent3 (HighContrast)</system:String>
|
||||
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
|
||||
<system:String x:Key="Theme.ColorScheme">Accent3</system:String>
|
||||
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
|
||||
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
|
||||
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
|
||||
<SolidColorBrush x:Key="QuaternaryBackgroundBrush" Color="#FF272727" />
|
||||
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
|
||||
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFffff00" />
|
||||
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FFc0c0c0" />
|
||||
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
|
||||
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
|
||||
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
|
||||
<SolidColorBrush x:Key="BackdropBrush" Color="#E55B5B5B" />
|
||||
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,26 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<!-- Metadata -->
|
||||
<system:String x:Key="Theme.Name">HighContrast.Accent4</system:String>
|
||||
<system:String x:Key="Theme.Origin">Origin</system:String>
|
||||
<system:String x:Key="Theme.DisplayName">Accent4 (HighContrast)</system:String>
|
||||
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
|
||||
<system:String x:Key="Theme.ColorScheme">Accent4</system:String>
|
||||
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
|
||||
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
|
||||
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
|
||||
<SolidColorBrush x:Key="QuaternaryBackgroundBrush" Color="#FF272727" />
|
||||
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
|
||||
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFffffff" />
|
||||
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF1aebff" />
|
||||
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
|
||||
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
|
||||
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
|
||||
<SolidColorBrush x:Key="BackdropBrush" Color="#E55B5B5B" />
|
||||
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,26 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<!-- Metadata -->
|
||||
<system:String x:Key="Theme.Name">HighContrast.Accent5</system:String>
|
||||
<system:String x:Key="Theme.Origin">Origin</system:String>
|
||||
<system:String x:Key="Theme.DisplayName">Accent5 (HighContrast)</system:String>
|
||||
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
|
||||
<system:String x:Key="Theme.ColorScheme">Accent5</system:String>
|
||||
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FFf9f9f9" />
|
||||
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FFeeeeee" />
|
||||
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FFF3F3F3" />
|
||||
<SolidColorBrush x:Key="QuaternaryBackgroundBrush" Color="#FFf6f6f6" />
|
||||
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
|
||||
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FF000000" />
|
||||
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF37006e" />
|
||||
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
|
||||
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
|
||||
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
|
||||
<SolidColorBrush x:Key="BackdropBrush" Color="#E5949494" />
|
||||
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF949494" />
|
||||
</ResourceDictionary>
|
||||
26
src/modules/Workspaces/WorkspacesEditor/Themes/Light.xaml
Normal file
26
src/modules/Workspaces/WorkspacesEditor/Themes/Light.xaml
Normal file
@@ -0,0 +1,26 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<!-- Metadata -->
|
||||
<system:String x:Key="Theme.Name">Light.Accent1</system:String>
|
||||
<system:String x:Key="Theme.Origin">Origin</system:String>
|
||||
<system:String x:Key="Theme.DisplayName">Accent1 (Light)</system:String>
|
||||
<system:String x:Key="Theme.BaseColorScheme">Light</system:String>
|
||||
<system:String x:Key="Theme.ColorScheme">Accent1</system:String>
|
||||
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FFf3f3f3" />
|
||||
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FFfbfbfb" />
|
||||
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FFfefefe" />
|
||||
<SolidColorBrush x:Key="QuaternaryBackgroundBrush" Color="#FFf6f6f6" />
|
||||
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FFF9F9F9" />
|
||||
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FF191919" />
|
||||
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF6A6A6A" />
|
||||
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FFe5e5e5" />
|
||||
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FFe5e5e5" />
|
||||
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FFe5e5e5" />
|
||||
<SolidColorBrush x:Key="BackdropBrush" Color="#85F0F0F0" />
|
||||
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF949494" />
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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 System.Text.Json;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class DashCaseNamingPolicy : JsonNamingPolicy
|
||||
{
|
||||
public static DashCaseNamingPolicy Instance { get; } = new DashCaseNamingPolicy();
|
||||
|
||||
public override string ConvertName(string name)
|
||||
{
|
||||
return name.UpperCamelCaseToDashCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
358
src/modules/Workspaces/WorkspacesEditor/Utils/DrawHelper.cs
Normal file
358
src/modules/Workspaces/WorkspacesEditor/Utils/DrawHelper.cs
Normal file
@@ -0,0 +1,358 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ManagedCommon;
|
||||
using WorkspacesEditor.Models;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class DrawHelper
|
||||
{
|
||||
private static Font font = new("Tahoma", 24);
|
||||
private static double scale = 0.1;
|
||||
private static double gapWidth;
|
||||
private static double gapHeight;
|
||||
|
||||
public static BitmapImage DrawPreview(Project project, Rectangle bounds, Theme currentTheme)
|
||||
{
|
||||
List<double> horizontalGaps = new List<double>();
|
||||
List<double> verticalGaps = new List<double>();
|
||||
gapWidth = bounds.Width * 0.01;
|
||||
gapHeight = bounds.Height * 0.01;
|
||||
|
||||
int Scaled(double value)
|
||||
{
|
||||
return (int)(value * scale);
|
||||
}
|
||||
|
||||
int TransformX(double posX)
|
||||
{
|
||||
double gapTransform = verticalGaps.Where(x => x <= posX).Count() * gapWidth;
|
||||
return Scaled(posX - bounds.Left + gapTransform);
|
||||
}
|
||||
|
||||
int TransformY(double posY)
|
||||
{
|
||||
double gapTransform = horizontalGaps.Where(x => x <= posY).Count() * gapHeight;
|
||||
return Scaled(posY - bounds.Top + gapTransform);
|
||||
}
|
||||
|
||||
Rectangle GetAppRect(Application app)
|
||||
{
|
||||
if (app.Maximized)
|
||||
{
|
||||
Project project = app.Parent;
|
||||
var monitor = project.Monitors.Where(x => x.MonitorNumber == app.MonitorNumber).FirstOrDefault();
|
||||
return new Rectangle(TransformX(monitor.MonitorDpiAwareBounds.Left), TransformY(monitor.MonitorDpiAwareBounds.Top), Scaled(monitor.MonitorDpiAwareBounds.Width), Scaled(monitor.MonitorDpiAwareBounds.Height));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Rectangle(TransformX(app.ScaledPosition.X), TransformY(app.ScaledPosition.Y), Scaled(app.ScaledPosition.Width), Scaled(app.ScaledPosition.Height));
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, int> repeatCounter = new Dictionary<string, int>();
|
||||
|
||||
var appsIncluded = project.Applications.Where(x => x.IsIncluded);
|
||||
|
||||
foreach (Application app in appsIncluded)
|
||||
{
|
||||
if (repeatCounter.TryGetValue(app.AppPath, out int value))
|
||||
{
|
||||
repeatCounter[app.AppPath] = ++value;
|
||||
}
|
||||
else
|
||||
{
|
||||
repeatCounter.Add(app.AppPath, 1);
|
||||
}
|
||||
|
||||
app.RepeatIndex = repeatCounter[app.AppPath];
|
||||
}
|
||||
|
||||
foreach (Application app in project.Applications.Where(x => !x.IsIncluded))
|
||||
{
|
||||
app.RepeatIndex = 0;
|
||||
}
|
||||
|
||||
// now that all repeat index values are set, update the repeat index strings on UI
|
||||
foreach (Application app in project.Applications)
|
||||
{
|
||||
app.OnPropertyChanged(new PropertyChangedEventArgs("RepeatIndexString"));
|
||||
}
|
||||
|
||||
foreach (MonitorSetup monitor in project.Monitors)
|
||||
{
|
||||
// check for vertical gap
|
||||
if (monitor.MonitorDpiAwareBounds.Left > bounds.Left && project.Monitors.Any(x => x.MonitorDpiAwareBounds.Right <= monitor.MonitorDpiAwareBounds.Left))
|
||||
{
|
||||
verticalGaps.Add(monitor.MonitorDpiAwareBounds.Left);
|
||||
}
|
||||
|
||||
// check for horizontal gap
|
||||
if (monitor.MonitorDpiAwareBounds.Top > bounds.Top && project.Monitors.Any(x => x.MonitorDpiAwareBounds.Bottom <= monitor.MonitorDpiAwareBounds.Top))
|
||||
{
|
||||
horizontalGaps.Add(monitor.MonitorDpiAwareBounds.Top);
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap previewBitmap = new Bitmap(Scaled(bounds.Width + (verticalGaps.Count * gapWidth)), Scaled((bounds.Height * 1.2) + (horizontalGaps.Count * gapHeight)));
|
||||
double desiredIconSize = Scaled(Math.Min(bounds.Width, bounds.Height)) * 0.25;
|
||||
using (Graphics g = Graphics.FromImage(previewBitmap))
|
||||
{
|
||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
Brush brush = new SolidBrush(currentTheme == Theme.Dark ? Color.FromArgb(10, 255, 255, 255) : Color.FromArgb(10, 0, 0, 0));
|
||||
Brush brushForHighlight = new SolidBrush(currentTheme == Theme.Dark ? Color.FromArgb(192, 255, 255, 255) : Color.FromArgb(192, 0, 0, 0));
|
||||
|
||||
// draw the monitors
|
||||
foreach (MonitorSetup monitor in project.Monitors)
|
||||
{
|
||||
Brush monitorBrush = new SolidBrush(currentTheme == Theme.Dark ? Color.FromArgb(32, 7, 91, 155) : Color.FromArgb(32, 7, 91, 155));
|
||||
g.FillRectangle(monitorBrush, new Rectangle(TransformX(monitor.MonitorDpiAwareBounds.Left), TransformY(monitor.MonitorDpiAwareBounds.Top), Scaled(monitor.MonitorDpiAwareBounds.Width), Scaled(monitor.MonitorDpiAwareBounds.Height)));
|
||||
}
|
||||
|
||||
var appsToDraw = appsIncluded.Where(x => !x.Minimized);
|
||||
|
||||
// draw the highlighted app at the end to have its icon in the foreground for the case there are overlapping icons
|
||||
foreach (Application app in appsToDraw.Where(x => !x.IsHighlighted))
|
||||
{
|
||||
Rectangle rect = GetAppRect(app);
|
||||
DrawWindow(g, brush, rect, app, desiredIconSize, currentTheme);
|
||||
}
|
||||
|
||||
foreach (Application app in appsToDraw.Where(x => x.IsHighlighted))
|
||||
{
|
||||
Rectangle rect = GetAppRect(app);
|
||||
DrawWindow(g, brushForHighlight, rect, app, desiredIconSize, currentTheme);
|
||||
}
|
||||
|
||||
// draw the minimized windows
|
||||
Rectangle rectMinimized = new Rectangle(0, Scaled((bounds.Height * 1.02) + (horizontalGaps.Count * gapHeight)), Scaled(bounds.Width + (verticalGaps.Count * gapWidth)), Scaled(bounds.Height * 0.18));
|
||||
DrawWindow(g, brush, brushForHighlight, rectMinimized, appsIncluded.Where(x => x.Minimized), currentTheme);
|
||||
}
|
||||
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
previewBitmap.Save(memory, ImageFormat.Png);
|
||||
memory.Position = 0;
|
||||
|
||||
var bitmapImage = new BitmapImage();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = memory;
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
bitmapImage.Freeze();
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawWindow(Graphics graphics, Brush brush, Rectangle bounds, Application app, double desiredIconSize, Theme currentTheme)
|
||||
{
|
||||
if (graphics == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (brush == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (GraphicsPath path = RoundedRect(bounds))
|
||||
{
|
||||
if (app.IsHighlighted)
|
||||
{
|
||||
graphics.DrawPath(new Pen(currentTheme == Theme.Dark ? Color.White : Color.DarkGray, graphics.VisibleClipBounds.Height / 50), path);
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.DrawPath(new Pen(currentTheme == Theme.Dark ? Color.FromArgb(128, 82, 82, 82) : Color.FromArgb(128, 160, 160, 160), graphics.VisibleClipBounds.Height / 200), path);
|
||||
}
|
||||
|
||||
graphics.FillPath(brush, path);
|
||||
}
|
||||
|
||||
double iconSize = Math.Min(Math.Min(bounds.Width - 4, bounds.Height - 4), desiredIconSize);
|
||||
Rectangle iconBounds = new Rectangle((int)(bounds.Left + (bounds.Width / 2) - (iconSize / 2)), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
|
||||
|
||||
try
|
||||
{
|
||||
graphics.DrawIcon(app.Icon, iconBounds);
|
||||
if (app.RepeatIndex > 1)
|
||||
{
|
||||
string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture);
|
||||
int indexSize = (int)(iconBounds.Width * 0.5);
|
||||
Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
|
||||
|
||||
var textSize = graphics.MeasureString(indexString, font);
|
||||
var state = graphics.Save();
|
||||
graphics.TranslateTransform(indexBounds.Left, indexBounds.Top);
|
||||
graphics.ScaleTransform(indexBounds.Width / textSize.Width, indexBounds.Height / textSize.Height);
|
||||
graphics.DrawString(indexString, font, Brushes.Black, PointF.Empty);
|
||||
graphics.Restore(state);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// sometimes drawing an icon throws an exception despite that the icon seems to be ok
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawWindow(Graphics graphics, Brush brush, Brush brushForHighlight, Rectangle bounds, IEnumerable<Application> apps, Theme currentTheme)
|
||||
{
|
||||
int appsCount = apps.Count();
|
||||
if (appsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (graphics == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (brush == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (GraphicsPath path = RoundedRect(bounds))
|
||||
{
|
||||
if (apps.Where(x => x.IsHighlighted).Any())
|
||||
{
|
||||
graphics.DrawPath(new Pen(currentTheme == Theme.Dark ? Color.White : Color.DarkGray, graphics.VisibleClipBounds.Height / 50), path);
|
||||
graphics.FillPath(brushForHighlight, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.DrawPath(new Pen(currentTheme == Theme.Dark ? Color.FromArgb(128, 82, 82, 82) : Color.FromArgb(128, 160, 160, 160), graphics.VisibleClipBounds.Height / 200), path);
|
||||
graphics.FillPath(brush, path);
|
||||
}
|
||||
}
|
||||
|
||||
double iconSize = Math.Min(bounds.Width, bounds.Height) * 0.5;
|
||||
for (int iconCounter = 0; iconCounter < appsCount; iconCounter++)
|
||||
{
|
||||
Application app = apps.ElementAt(iconCounter);
|
||||
Rectangle iconBounds = new Rectangle((int)(bounds.Left + (bounds.Width / 2) - (iconSize * ((appsCount / 2) - iconCounter))), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
|
||||
|
||||
try
|
||||
{
|
||||
graphics.DrawIcon(app.Icon, iconBounds);
|
||||
if (app.RepeatIndex > 0)
|
||||
{
|
||||
string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture);
|
||||
int indexSize = (int)(iconBounds.Width * 0.5);
|
||||
Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
|
||||
|
||||
var textSize = graphics.MeasureString(indexString, font);
|
||||
var state = graphics.Save();
|
||||
graphics.TranslateTransform(indexBounds.Left, indexBounds.Top);
|
||||
graphics.ScaleTransform(indexBounds.Width / textSize.Width, indexBounds.Height / textSize.Height);
|
||||
graphics.DrawString(indexString, font, Brushes.Black, PointF.Empty);
|
||||
graphics.Restore(state);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// sometimes drawing an icon throws an exception despite that the icon seems to be ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static BitmapImage DrawPreviewIcons(Project project)
|
||||
{
|
||||
int appsCount = project.Applications.Count;
|
||||
if (appsCount == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Bitmap previewBitmap = new Bitmap(32 * appsCount, 24);
|
||||
using (Graphics graphics = Graphics.FromImage(previewBitmap))
|
||||
{
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
int appIndex = 0;
|
||||
foreach (var app in project.Applications)
|
||||
{
|
||||
try
|
||||
{
|
||||
graphics.DrawIcon(app.Icon, new Rectangle(32 * appIndex, 0, 24, 24));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Exception while drawing the icon for app {app.AppName}. Exception message: {e.Message}");
|
||||
}
|
||||
|
||||
appIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
previewBitmap.Save(memory, ImageFormat.Png);
|
||||
memory.Position = 0;
|
||||
|
||||
var bitmapImage = new BitmapImage();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = memory;
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
bitmapImage.Freeze();
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
}
|
||||
|
||||
private static GraphicsPath RoundedRect(Rectangle bounds)
|
||||
{
|
||||
int minorSize = Math.Min(bounds.Width, bounds.Height);
|
||||
int radius = (int)(minorSize / 8);
|
||||
|
||||
int diameter = radius * 2;
|
||||
Size size = new Size(diameter, diameter);
|
||||
Rectangle arc = new Rectangle(bounds.Location, size);
|
||||
GraphicsPath path = new GraphicsPath();
|
||||
|
||||
if (radius == 0)
|
||||
{
|
||||
path.AddRectangle(bounds);
|
||||
return path;
|
||||
}
|
||||
|
||||
// top left arc
|
||||
path.AddArc(arc, 180, 90);
|
||||
|
||||
// top right arc
|
||||
arc.X = bounds.Right - diameter;
|
||||
path.AddArc(arc, 270, 90);
|
||||
|
||||
// bottom right arc
|
||||
arc.Y = bounds.Bottom - diameter;
|
||||
path.AddArc(arc, 0, 90);
|
||||
|
||||
// bottom left arc
|
||||
arc.X = bounds.Left;
|
||||
path.AddArc(arc, 90, 90);
|
||||
|
||||
path.CloseFigure();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/modules/Workspaces/WorkspacesEditor/Utils/FolderUtils.cs
Normal file
28
src/modules/Workspaces/WorkspacesEditor/Utils/FolderUtils.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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 System;
|
||||
using System.IO;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class FolderUtils
|
||||
{
|
||||
public static string Desktop()
|
||||
{
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
}
|
||||
|
||||
public static string Temp()
|
||||
{
|
||||
return Path.GetTempPath();
|
||||
}
|
||||
|
||||
// Note: the same path should be used in SnapshotTool and Launcher
|
||||
public static string DataFolder()
|
||||
{
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Microsoft\\PowerToys\\Workspaces";
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/modules/Workspaces/WorkspacesEditor/Utils/IOUtils.cs
Normal file
54
src/modules/Workspaces/WorkspacesEditor/Utils/IOUtils.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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 System;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class IOUtils
|
||||
{
|
||||
private readonly IFileSystem _fileSystem = new FileSystem();
|
||||
|
||||
public IOUtils()
|
||||
{
|
||||
}
|
||||
|
||||
public void WriteFile(string fileName, string data)
|
||||
{
|
||||
_fileSystem.File.WriteAllText(fileName, data);
|
||||
}
|
||||
|
||||
public string ReadFile(string fileName)
|
||||
{
|
||||
if (_fileSystem.File.Exists(fileName))
|
||||
{
|
||||
var attempts = 0;
|
||||
while (attempts < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Stream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
|
||||
using (StreamReader reader = new StreamReader(inputStream))
|
||||
{
|
||||
string data = reader.ReadToEnd();
|
||||
inputStream.Close();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Task.Delay(10).Wait();
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class MonitorHelper
|
||||
{
|
||||
private const int DpiAwarenessContextUnaware = -1;
|
||||
|
||||
private Screen[] screens;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext);
|
||||
|
||||
private void SaveDpiUnawareScreens()
|
||||
{
|
||||
SetThreadDpiAwarenessContext(DpiAwarenessContextUnaware);
|
||||
screens = Screen.AllScreens;
|
||||
}
|
||||
|
||||
private Screen[] GetDpiUnawareScreenBounds()
|
||||
{
|
||||
Thread dpiUnawareThread = new Thread(new ThreadStart(SaveDpiUnawareScreens));
|
||||
dpiUnawareThread.Start();
|
||||
dpiUnawareThread.Join();
|
||||
|
||||
return screens;
|
||||
}
|
||||
|
||||
public static Screen[] GetDpiUnawareScreens()
|
||||
{
|
||||
MonitorHelper monitorHelper = new MonitorHelper();
|
||||
return monitorHelper.GetDpiUnawareScreenBounds();
|
||||
}
|
||||
|
||||
internal static double GetScreenDpiFromScreen(Screen screen)
|
||||
{
|
||||
var point = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
|
||||
var monitor = NativeMethods.MonitorFromPoint(point, NativeMethods._MONITOR_DEFAULTTONEAREST);
|
||||
NativeMethods.GetDpiForMonitor(monitor, NativeMethods.DpiType.EFFECTIVE, out uint dpiX, out uint dpiY);
|
||||
return dpiX / 96.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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 System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
internal sealed class NativeMethods
|
||||
{
|
||||
public const int SW_RESTORE = 9;
|
||||
public const int SW_NORMAL = 1;
|
||||
public const int SW_MINIMIZE = 6;
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
|
||||
|
||||
[DllImport("USER32.DLL")]
|
||||
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern uint GetCurrentThreadId();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
||||
|
||||
public enum DpiType
|
||||
{
|
||||
EFFECTIVE = 0,
|
||||
ANGULAR = 1,
|
||||
RAW = 2,
|
||||
}
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
public static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
|
||||
|
||||
[DllImport("Shcore.dll")]
|
||||
public static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
|
||||
|
||||
public const int _S_OK = 0;
|
||||
public const int _MONITOR_DEFAULTTONEAREST = 2;
|
||||
public const int _E_INVALIDARG = -2147024809;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public struct ParsingResult
|
||||
{
|
||||
public bool Result { get; }
|
||||
|
||||
public string Message { get; }
|
||||
|
||||
public string MalformedData { get; }
|
||||
|
||||
public ParsingResult(bool result, string message = "", string data = "")
|
||||
{
|
||||
Result = result;
|
||||
Message = message;
|
||||
MalformedData = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/modules/Workspaces/WorkspacesEditor/Utils/Settings.cs
Normal file
27
src/modules/Workspaces/WorkspacesEditor/Utils/Settings.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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.PowerToys.Settings.UI.Library;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class Settings
|
||||
{
|
||||
private const string WorkspacesModuleName = "Workspaces";
|
||||
private static SettingsUtils _settingsUtils = new SettingsUtils();
|
||||
|
||||
public static WorkspacesSettings ReadSettings()
|
||||
{
|
||||
if (!_settingsUtils.SettingsExists(WorkspacesModuleName))
|
||||
{
|
||||
var defaultWorkspacesSettings = new WorkspacesSettings();
|
||||
defaultWorkspacesSettings.Save(_settingsUtils);
|
||||
return defaultWorkspacesSettings;
|
||||
}
|
||||
|
||||
WorkspacesSettings settings = _settingsUtils.GetSettingsOrDefault<WorkspacesSettings>(WorkspacesModuleName);
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/modules/Workspaces/WorkspacesEditor/Utils/StringUtils.cs
Normal file
22
src/modules/Workspaces/WorkspacesEditor/Utils/StringUtils.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
// 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 System.Linq;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public static class StringUtils
|
||||
{
|
||||
public static string UpperCamelCaseToDashCase(this string str)
|
||||
{
|
||||
// If it's single letter variable, leave it as it is
|
||||
if (str.Length == 1)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "-" + x.ToString() : x.ToString())).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ManagedCommon;
|
||||
using WorkspacesEditor.Data;
|
||||
using WorkspacesEditor.Models;
|
||||
using WorkspacesEditor.ViewModels;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class WorkspacesEditorIO
|
||||
{
|
||||
public WorkspacesEditorIO()
|
||||
{
|
||||
}
|
||||
|
||||
public ParsingResult ParseWorkspaces(MainViewModel mainViewModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
WorkspacesData parser = new WorkspacesData();
|
||||
if (!File.Exists(parser.File))
|
||||
{
|
||||
Logger.LogWarning($"Workspaces storage file not found: {parser.File}");
|
||||
return new ParsingResult(true);
|
||||
}
|
||||
|
||||
WorkspacesData.WorkspacesListWrapper workspaces = parser.Read(parser.File);
|
||||
if (workspaces.Workspaces == null)
|
||||
{
|
||||
return new ParsingResult(true);
|
||||
}
|
||||
|
||||
if (!SetWorkspaces(mainViewModel, workspaces))
|
||||
{
|
||||
Logger.LogWarning($"Workspaces storage file content could not be set. Reason: {Properties.Resources.Error_Parsing_Message}");
|
||||
return new ParsingResult(false, WorkspacesEditor.Properties.Resources.Error_Parsing_Message);
|
||||
}
|
||||
|
||||
return new ParsingResult(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Exception while parsing storage file: {e.Message}");
|
||||
return new ParsingResult(false, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public Project ParseTempProject()
|
||||
{
|
||||
try
|
||||
{
|
||||
ProjectData parser = new ProjectData();
|
||||
if (!File.Exists(TempProjectData.File))
|
||||
{
|
||||
Logger.LogWarning($"ParseProject method. Workspaces storage file not found: {TempProjectData.File}");
|
||||
return null;
|
||||
}
|
||||
|
||||
Project project = new Project(parser.Read(TempProjectData.File));
|
||||
return project;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"ParseProject method. Exception while parsing storage file: {e.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SerializeWorkspaces(List<Project> workspaces, bool useTempFile = false)
|
||||
{
|
||||
WorkspacesData serializer = new WorkspacesData();
|
||||
WorkspacesData.WorkspacesListWrapper workspacesWrapper = new WorkspacesData.WorkspacesListWrapper { };
|
||||
workspacesWrapper.Workspaces = new List<ProjectData.ProjectWrapper>();
|
||||
|
||||
foreach (Project project in workspaces)
|
||||
{
|
||||
ProjectData.ProjectWrapper wrapper = new ProjectData.ProjectWrapper
|
||||
{
|
||||
Id = project.Id,
|
||||
Name = project.Name,
|
||||
CreationTime = project.CreationTime,
|
||||
IsShortcutNeeded = project.IsShortcutNeeded,
|
||||
MoveExistingWindows = project.MoveExistingWindows,
|
||||
LastLaunchedTime = project.LastLaunchedTime,
|
||||
Applications = new List<ProjectData.ApplicationWrapper> { },
|
||||
MonitorConfiguration = new List<ProjectData.MonitorConfigurationWrapper> { },
|
||||
};
|
||||
|
||||
foreach (var app in project.Applications.Where(x => x.IsIncluded))
|
||||
{
|
||||
wrapper.Applications.Add(new ProjectData.ApplicationWrapper
|
||||
{
|
||||
Application = app.AppName,
|
||||
ApplicationPath = app.AppPath,
|
||||
Title = app.AppTitle,
|
||||
PackageFullName = app.PackageFullName,
|
||||
AppUserModelId = app.AppUserModelId,
|
||||
CommandLineArguments = app.CommandLineArguments,
|
||||
IsElevated = app.IsElevated,
|
||||
CanLaunchElevated = app.CanLaunchElevated,
|
||||
Maximized = app.Maximized,
|
||||
Minimized = app.Minimized,
|
||||
Position = new ProjectData.ApplicationWrapper.WindowPositionWrapper
|
||||
{
|
||||
X = app.Position.X,
|
||||
Y = app.Position.Y,
|
||||
Height = app.Position.Height,
|
||||
Width = app.Position.Width,
|
||||
},
|
||||
Monitor = app.MonitorNumber,
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var monitor in project.Monitors)
|
||||
{
|
||||
wrapper.MonitorConfiguration.Add(new ProjectData.MonitorConfigurationWrapper
|
||||
{
|
||||
Id = monitor.MonitorName,
|
||||
InstanceId = monitor.MonitorInstanceId,
|
||||
MonitorNumber = monitor.MonitorNumber,
|
||||
Dpi = monitor.Dpi,
|
||||
MonitorRectDpiAware = new ProjectData.MonitorConfigurationWrapper.MonitorRectWrapper
|
||||
{
|
||||
Left = (int)monitor.MonitorDpiAwareBounds.Left,
|
||||
Top = (int)monitor.MonitorDpiAwareBounds.Top,
|
||||
Width = (int)monitor.MonitorDpiAwareBounds.Width,
|
||||
Height = (int)monitor.MonitorDpiAwareBounds.Height,
|
||||
},
|
||||
MonitorRectDpiUnaware = new ProjectData.MonitorConfigurationWrapper.MonitorRectWrapper
|
||||
{
|
||||
Left = (int)monitor.MonitorDpiUnawareBounds.Left,
|
||||
Top = (int)monitor.MonitorDpiUnawareBounds.Top,
|
||||
Width = (int)monitor.MonitorDpiUnawareBounds.Width,
|
||||
Height = (int)monitor.MonitorDpiUnawareBounds.Height,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
workspacesWrapper.Workspaces.Add(wrapper);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IOUtils ioUtils = new IOUtils();
|
||||
ioUtils.WriteFile(useTempFile ? TempProjectData.File : serializer.File, serializer.Serialize(workspacesWrapper));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// TODO: show error
|
||||
Logger.LogError($"Exception while writing storage file: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool AddWorkspaces(MainViewModel mainViewModel, WorkspacesData.WorkspacesListWrapper workspaces)
|
||||
{
|
||||
foreach (var project in workspaces.Workspaces)
|
||||
{
|
||||
mainViewModel.Workspaces.Add(new Project(project));
|
||||
}
|
||||
|
||||
mainViewModel.Initialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool SetWorkspaces(MainViewModel mainViewModel, WorkspacesData.WorkspacesListWrapper workspaces)
|
||||
{
|
||||
mainViewModel.Workspaces = new System.Collections.ObjectModel.ObservableCollection<Project> { };
|
||||
return AddWorkspaces(mainViewModel, workspaces);
|
||||
}
|
||||
|
||||
internal void SerializeTempProject(Project project)
|
||||
{
|
||||
SerializeWorkspaces(new List<Project>() { project }, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
153
src/modules/Workspaces/WorkspacesEditor/Utils/WorkspacesIcon.cs
Normal file
153
src/modules/Workspaces/WorkspacesEditor/Utils/WorkspacesIcon.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
// 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 System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class WorkspacesIcon : IDisposable
|
||||
{
|
||||
private const int IconSize = 128;
|
||||
|
||||
public static readonly Brush LightThemeIconBackground = new SolidBrush(Color.FromArgb(255, 239, 243, 251));
|
||||
public static readonly Brush LightThemeIconForeground = new SolidBrush(Color.FromArgb(255, 47, 50, 56));
|
||||
public static readonly Brush DarkThemeIconBackground = new SolidBrush(Color.FromArgb(255, 55, 55, 55));
|
||||
public static readonly Brush DarkThemeIconForeground = new SolidBrush(Color.FromArgb(255, 228, 228, 228));
|
||||
|
||||
public static readonly Font IconFont = new("Aptos", 24, FontStyle.Bold);
|
||||
|
||||
public static string IconTextFromProjectName(string projectName)
|
||||
{
|
||||
string result = string.Empty;
|
||||
char[] delimiterChars = { ' ', ',', '.', ':', '-', '\t' };
|
||||
string[] words = projectName.Split(delimiterChars);
|
||||
|
||||
foreach (string word in words)
|
||||
{
|
||||
if (string.IsNullOrEmpty(word))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (word.All(char.IsDigit))
|
||||
{
|
||||
result += word;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += word.ToUpper(System.Globalization.CultureInfo.CurrentCulture).ToCharArray()[0];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Bitmap DrawIcon(string text, Theme currentTheme)
|
||||
{
|
||||
Brush background = currentTheme == Theme.Dark ? DarkThemeIconBackground : LightThemeIconBackground;
|
||||
Brush foreground = currentTheme == Theme.Dark ? DarkThemeIconForeground : LightThemeIconForeground;
|
||||
Bitmap bitmap = new Bitmap(IconSize, IconSize);
|
||||
|
||||
using (Graphics graphics = Graphics.FromImage(bitmap))
|
||||
{
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
graphics.FillEllipse(background, 0, 0, IconSize, IconSize);
|
||||
|
||||
var textSize = graphics.MeasureString(text, IconFont);
|
||||
var state = graphics.Save();
|
||||
|
||||
// Calculate scaling factors
|
||||
float scaleX = (float)IconSize / textSize.Width;
|
||||
float scaleY = (float)IconSize / textSize.Height;
|
||||
float scale = Math.Min(scaleX, scaleY) * 0.8f; // Use the smaller scale factor to maintain aspect ratio
|
||||
|
||||
// Calculate the position to center the text
|
||||
float textX = (IconSize - (textSize.Width * scale)) / 2;
|
||||
float textY = ((IconSize - (textSize.Height * scale)) / 2) + 6;
|
||||
|
||||
graphics.TranslateTransform(textX, textY);
|
||||
graphics.ScaleTransform(scale, scale);
|
||||
graphics.DrawString(text, IconFont, foreground, 0, 0);
|
||||
graphics.Restore(state);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static void SaveIcon(Bitmap icon, string path)
|
||||
{
|
||||
if (Path.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
FileStream fileStream = new FileStream(path, FileMode.CreateNew);
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
icon.Save(memoryStream, ImageFormat.Png);
|
||||
|
||||
BinaryWriter iconWriter = new BinaryWriter(fileStream);
|
||||
if (fileStream != null && iconWriter != null)
|
||||
{
|
||||
// 0-1 reserved, 0
|
||||
iconWriter.Write((byte)0);
|
||||
iconWriter.Write((byte)0);
|
||||
|
||||
// 2-3 image type, 1 = icon, 2 = cursor
|
||||
iconWriter.Write((short)1);
|
||||
|
||||
// 4-5 number of images
|
||||
iconWriter.Write((short)1);
|
||||
|
||||
// image entry 1
|
||||
// 0 image width
|
||||
iconWriter.Write((byte)IconSize);
|
||||
|
||||
// 1 image height
|
||||
iconWriter.Write((byte)IconSize);
|
||||
|
||||
// 2 number of colors
|
||||
iconWriter.Write((byte)0);
|
||||
|
||||
// 3 reserved
|
||||
iconWriter.Write((byte)0);
|
||||
|
||||
// 4-5 color planes
|
||||
iconWriter.Write((short)0);
|
||||
|
||||
// 6-7 bits per pixel
|
||||
iconWriter.Write((short)32);
|
||||
|
||||
// 8-11 size of image data
|
||||
iconWriter.Write((int)memoryStream.Length);
|
||||
|
||||
// 12-15 offset of image data
|
||||
iconWriter.Write((int)(6 + 16));
|
||||
|
||||
// write image data
|
||||
// png data must contain the whole png data file
|
||||
iconWriter.Write(memoryStream.ToArray());
|
||||
|
||||
iconWriter.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
fileStream.Flush();
|
||||
fileStream.Close();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using System.Windows;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using WorkspacesEditor.Data;
|
||||
using WorkspacesEditor.Models;
|
||||
using WorkspacesEditor.Telemetry;
|
||||
using WorkspacesEditor.Utils;
|
||||
using static WorkspacesEditor.Data.WorkspacesData;
|
||||
|
||||
namespace WorkspacesEditor.ViewModels
|
||||
{
|
||||
public class MainViewModel : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
private WorkspacesEditorIO _workspacesEditorIO;
|
||||
private ProjectEditor editPage;
|
||||
private SnapshotWindow _snapshotWindow;
|
||||
private List<OverlayWindow> _overlayWindows = new List<OverlayWindow>();
|
||||
private Project editedProject;
|
||||
private Project projectBeforeLaunch;
|
||||
private string projectNameBeingEdited;
|
||||
private MainWindow _mainWindow;
|
||||
private Timer lastUpdatedTimer;
|
||||
private WorkspacesSettings settings;
|
||||
|
||||
public ObservableCollection<Project> Workspaces { get; set; } = new ObservableCollection<Project>();
|
||||
|
||||
public IEnumerable<Project> WorkspacesView
|
||||
{
|
||||
get
|
||||
{
|
||||
IEnumerable<Project> workspaces = GetFilteredWorkspaces();
|
||||
IsWorkspacesViewEmpty = !(workspaces != null && workspaces.Any());
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsWorkspacesViewEmpty)));
|
||||
if (IsWorkspacesViewEmpty)
|
||||
{
|
||||
if (Workspaces != null && Workspaces.Any())
|
||||
{
|
||||
EmptyWorkspacesViewMessage = Properties.Resources.NoWorkspacesMatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
EmptyWorkspacesViewMessage = Properties.Resources.No_Workspaces_Message;
|
||||
}
|
||||
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(EmptyWorkspacesViewMessage)));
|
||||
|
||||
return Enumerable.Empty<Project>();
|
||||
}
|
||||
|
||||
OrderBy orderBy = (OrderBy)_orderByIndex;
|
||||
if (orderBy == OrderBy.LastViewed)
|
||||
{
|
||||
return workspaces.OrderByDescending(x => x.LastLaunchedTime);
|
||||
}
|
||||
else if (orderBy == OrderBy.Created)
|
||||
{
|
||||
return workspaces.OrderByDescending(x => x.CreationTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
return workspaces.OrderBy(x => x.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWorkspacesViewEmpty { get; set; }
|
||||
|
||||
public string EmptyWorkspacesViewMessage { get; set; }
|
||||
|
||||
// return those workspaces where the project name or any of the selected apps' name contains the search term
|
||||
private IEnumerable<Project> GetFilteredWorkspaces()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_searchTerm))
|
||||
{
|
||||
return Workspaces;
|
||||
}
|
||||
|
||||
return Workspaces.Where(x =>
|
||||
{
|
||||
if (x.Name.Contains(_searchTerm, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (x.Applications == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return x.Applications.Any(app => app.AppName.Contains(_searchTerm, StringComparison.InvariantCultureIgnoreCase));
|
||||
});
|
||||
}
|
||||
|
||||
private string _searchTerm;
|
||||
|
||||
public string SearchTerm
|
||||
{
|
||||
get => _searchTerm;
|
||||
set
|
||||
{
|
||||
_searchTerm = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
|
||||
}
|
||||
}
|
||||
|
||||
private int _orderByIndex;
|
||||
|
||||
public int OrderByIndex
|
||||
{
|
||||
get => _orderByIndex;
|
||||
set
|
||||
{
|
||||
_orderByIndex = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
|
||||
settings.Properties.SortBy = (WorkspacesProperties.SortByProperty)value;
|
||||
settings.Save(new SettingsUtils());
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public MainViewModel(WorkspacesEditorIO workspacesEditorIO)
|
||||
{
|
||||
settings = Utils.Settings.ReadSettings();
|
||||
_orderByIndex = (int)settings.Properties.SortBy;
|
||||
_workspacesEditorIO = workspacesEditorIO;
|
||||
lastUpdatedTimer = new System.Timers.Timer();
|
||||
lastUpdatedTimer.Interval = 1000;
|
||||
lastUpdatedTimer.Elapsed += LastUpdatedTimerElapsed;
|
||||
lastUpdatedTimer.Start();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
foreach (Project project in Workspaces)
|
||||
{
|
||||
project.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
|
||||
}
|
||||
|
||||
public void SetEditedProject(Project editedProject)
|
||||
{
|
||||
this.editedProject = editedProject;
|
||||
}
|
||||
|
||||
public void SaveProject(Project projectToSave)
|
||||
{
|
||||
SendEditTelemetryEvent(projectToSave, editedProject);
|
||||
|
||||
if (editedProject.Name != projectToSave.Name)
|
||||
{
|
||||
RemoveShortcut(editedProject);
|
||||
}
|
||||
|
||||
editedProject.Name = projectToSave.Name;
|
||||
editedProject.IsShortcutNeeded = projectToSave.IsShortcutNeeded;
|
||||
editedProject.MoveExistingWindows = projectToSave.MoveExistingWindows;
|
||||
editedProject.PreviewIcons = projectToSave.PreviewIcons;
|
||||
editedProject.PreviewImage = projectToSave.PreviewImage;
|
||||
editedProject.Applications = projectToSave.Applications.Where(x => x.IsIncluded).ToList();
|
||||
|
||||
editedProject.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("AppsCountString"));
|
||||
editedProject.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
_workspacesEditorIO.SerializeWorkspaces(Workspaces.ToList());
|
||||
ApplyShortcut(editedProject);
|
||||
}
|
||||
|
||||
private void ApplyShortcut(Project project)
|
||||
{
|
||||
string basePath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string shortcutAddress = Path.Combine(FolderUtils.Desktop(), project.Name + ".lnk");
|
||||
string shortcutIconFilename = Path.Combine(FolderUtils.Temp(), project.Id + ".ico");
|
||||
|
||||
if (!project.IsShortcutNeeded)
|
||||
{
|
||||
if (File.Exists(shortcutIconFilename))
|
||||
{
|
||||
File.Delete(shortcutIconFilename);
|
||||
}
|
||||
|
||||
if (File.Exists(shortcutAddress))
|
||||
{
|
||||
File.Delete(shortcutAddress);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap icon = WorkspacesIcon.DrawIcon(WorkspacesIcon.IconTextFromProjectName(project.Name), App.ThemeManager.GetCurrentTheme());
|
||||
WorkspacesIcon.SaveIcon(icon, shortcutIconFilename);
|
||||
|
||||
try
|
||||
{
|
||||
// Workaround to be able to create a shortcut with unicode filename
|
||||
File.WriteAllBytes(shortcutAddress, Array.Empty<byte>());
|
||||
|
||||
// Create a ShellLinkObject that references the .lnk file
|
||||
Shell32.Shell shell = new Shell32.Shell();
|
||||
Shell32.Folder dir = shell.NameSpace(FolderUtils.Desktop());
|
||||
Shell32.FolderItem folderItem = dir.Items().Item($"{project.Name}.lnk");
|
||||
Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)folderItem.GetLink;
|
||||
|
||||
// Set the .lnk file properties
|
||||
link.Description = $"Project Launcher {project.Id}";
|
||||
link.Path = Path.Combine(basePath, "PowerToys.WorkspacesLauncher.exe");
|
||||
link.Arguments = $"{project.Id.ToString()} {(int)InvokePoint.Shortcut}";
|
||||
link.WorkingDirectory = basePath;
|
||||
link.SetIconLocation(shortcutIconFilename, 0);
|
||||
|
||||
link.Save(shortcutAddress);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Shortcut creation error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveProjectName(Project project)
|
||||
{
|
||||
projectNameBeingEdited = project.Name;
|
||||
}
|
||||
|
||||
public void CancelProjectName(Project project)
|
||||
{
|
||||
project.Name = projectNameBeingEdited;
|
||||
}
|
||||
|
||||
public async void SnapWorkspace()
|
||||
{
|
||||
CancelSnapshot();
|
||||
|
||||
await Task.Run(() => RunSnapshotTool());
|
||||
|
||||
Project project = _workspacesEditorIO.ParseTempProject();
|
||||
if (project != null)
|
||||
{
|
||||
if (editedProject != null)
|
||||
{
|
||||
project.UpdateAfterLaunchAndEdit(projectBeforeLaunch);
|
||||
project.EditorWindowTitle = Properties.Resources.EditWorkspace;
|
||||
editPage.DataContext = project;
|
||||
CheckShortcutPresence(project);
|
||||
project.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
else
|
||||
{
|
||||
EditProject(project, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RevertLaunch()
|
||||
{
|
||||
CheckShortcutPresence(projectBeforeLaunch);
|
||||
editPage.DataContext = projectBeforeLaunch;
|
||||
projectBeforeLaunch.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
|
||||
public void EditProject(Project selectedProject, bool isNewlyCreated = false)
|
||||
{
|
||||
editPage = new ProjectEditor(this);
|
||||
|
||||
SetEditedProject(selectedProject);
|
||||
if (!isNewlyCreated)
|
||||
{
|
||||
selectedProject = new Project(selectedProject);
|
||||
}
|
||||
|
||||
if (isNewlyCreated)
|
||||
{
|
||||
// generate a default name for the new project
|
||||
string defaultNamePrefix = Properties.Resources.DefaultWorkspaceNamePrefix;
|
||||
int nextProjectIndex = 0;
|
||||
foreach (var proj in Workspaces)
|
||||
{
|
||||
if (proj.Name.StartsWith(defaultNamePrefix, StringComparison.CurrentCulture))
|
||||
{
|
||||
try
|
||||
{
|
||||
int index = int.Parse(proj.Name[(defaultNamePrefix.Length + 1)..], CultureInfo.CurrentCulture);
|
||||
if (nextProjectIndex < index)
|
||||
{
|
||||
nextProjectIndex = index;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedProject.Name = defaultNamePrefix + " " + (nextProjectIndex + 1).ToString(CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
selectedProject.EditorWindowTitle = isNewlyCreated ? Properties.Resources.CreateWorkspace : Properties.Resources.EditWorkspace;
|
||||
selectedProject.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
|
||||
CheckShortcutPresence(selectedProject);
|
||||
|
||||
editPage.DataContext = selectedProject;
|
||||
_mainWindow.ShowPage(editPage);
|
||||
lastUpdatedTimer.Stop();
|
||||
}
|
||||
|
||||
private void CheckShortcutPresence(Project project)
|
||||
{
|
||||
string basePath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string shortcutAddress = Path.Combine(FolderUtils.Desktop(), project.Name + ".lnk");
|
||||
project.IsShortcutNeeded = File.Exists(shortcutAddress);
|
||||
}
|
||||
|
||||
public void AddNewProject(Project project)
|
||||
{
|
||||
project.Applications.RemoveAll(app => !app.IsIncluded);
|
||||
project.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
Workspaces.Add(project);
|
||||
_workspacesEditorIO.SerializeWorkspaces(Workspaces.ToList());
|
||||
TempProjectData.DeleteTempFile();
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
|
||||
ApplyShortcut(project);
|
||||
SendCreateTelemetryEvent(project);
|
||||
}
|
||||
|
||||
public void DeleteProject(Project selectedProject)
|
||||
{
|
||||
Workspaces.Remove(selectedProject);
|
||||
_workspacesEditorIO.SerializeWorkspaces(Workspaces.ToList());
|
||||
RemoveShortcut(selectedProject);
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
|
||||
SendDeleteTelemetryEvent();
|
||||
}
|
||||
|
||||
private void RemoveShortcut(Project selectedProject)
|
||||
{
|
||||
string shortcutAddress = Path.Combine(FolderUtils.Desktop(), selectedProject.Name + ".lnk");
|
||||
string shortcutIconFilename = Path.Combine(FolderUtils.Temp(), selectedProject.Id + ".ico");
|
||||
|
||||
if (File.Exists(shortcutIconFilename))
|
||||
{
|
||||
File.Delete(shortcutIconFilename);
|
||||
}
|
||||
|
||||
if (File.Exists(shortcutAddress))
|
||||
{
|
||||
File.Delete(shortcutAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMainWindow(MainWindow mainWindow)
|
||||
{
|
||||
_mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
public void SwitchToMainView()
|
||||
{
|
||||
_mainWindow.SwitchToMainView();
|
||||
SearchTerm = string.Empty;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(SearchTerm)));
|
||||
lastUpdatedTimer.Start();
|
||||
editedProject = null;
|
||||
}
|
||||
|
||||
public void LaunchProject(string projectId)
|
||||
{
|
||||
if (!Workspaces.Where(x => x.Id == projectId).Any())
|
||||
{
|
||||
Logger.LogWarning($"Workspace to launch not found. Id: {projectId}");
|
||||
return;
|
||||
}
|
||||
|
||||
LaunchProject(Workspaces.Where(x => x.Id == projectId).First(), true);
|
||||
}
|
||||
|
||||
public async void LaunchProject(Project project, bool exitAfterLaunch = false)
|
||||
{
|
||||
await Task.Run(() => RunLauncher(project.Id, InvokePoint.EditorButton));
|
||||
if (_workspacesEditorIO.ParseWorkspaces(this).Result == true)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
|
||||
}
|
||||
|
||||
if (exitAfterLaunch)
|
||||
{
|
||||
Logger.LogInfo($"Launched the Workspace {project.Name}. Exiting.");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void LastUpdatedTimerElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (Workspaces == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Project project in Workspaces)
|
||||
{
|
||||
project.OnPropertyChanged(new PropertyChangedEventArgs("LastLaunched"));
|
||||
}
|
||||
}
|
||||
|
||||
private void RunSnapshotTool(string filename = null)
|
||||
{
|
||||
Process process = new Process();
|
||||
process.StartInfo = new ProcessStartInfo(@".\PowerToys.WorkspacesSnapshotTool.exe");
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
if (!string.IsNullOrEmpty(filename))
|
||||
{
|
||||
process.StartInfo.Arguments = filename;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
process.WaitForExit();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"An error occurred: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void RunLauncher(string projectId, InvokePoint invokePoint)
|
||||
{
|
||||
Process process = new Process();
|
||||
process.StartInfo = new ProcessStartInfo(@".\PowerToys.WorkspacesLauncher.exe", $"{projectId} {(int)invokePoint}");
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
process.WaitForExit();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"An error occurred: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
internal void CloseAllPopups()
|
||||
{
|
||||
foreach (Project project in Workspaces)
|
||||
{
|
||||
project.IsPopupVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void EnterSnapshotMode(bool isExistingProjectLaunched)
|
||||
{
|
||||
_mainWindow.WindowState = System.Windows.WindowState.Minimized;
|
||||
_overlayWindows.Clear();
|
||||
foreach (var screen in MonitorHelper.GetDpiUnawareScreens())
|
||||
{
|
||||
var bounds = screen.Bounds;
|
||||
OverlayWindow overlayWindow = new OverlayWindow();
|
||||
overlayWindow.Top = bounds.Top;
|
||||
overlayWindow.Left = bounds.Left;
|
||||
overlayWindow.Width = bounds.Width;
|
||||
overlayWindow.Height = bounds.Height;
|
||||
overlayWindow.ShowActivated = true;
|
||||
overlayWindow.Topmost = true;
|
||||
overlayWindow.Show();
|
||||
_overlayWindows.Add(overlayWindow);
|
||||
}
|
||||
|
||||
_snapshotWindow = new SnapshotWindow(this);
|
||||
_snapshotWindow.ShowActivated = true;
|
||||
_snapshotWindow.Topmost = true;
|
||||
_snapshotWindow.Show();
|
||||
}
|
||||
|
||||
internal void CancelSnapshot()
|
||||
{
|
||||
foreach (OverlayWindow overlayWindow in _overlayWindows)
|
||||
{
|
||||
overlayWindow.Close();
|
||||
}
|
||||
|
||||
_mainWindow.WindowState = System.Windows.WindowState.Normal;
|
||||
}
|
||||
|
||||
internal async void LaunchAndEdit(Project project)
|
||||
{
|
||||
await Task.Run(() => RunLauncher(project.Id, InvokePoint.LaunchAndEdit));
|
||||
projectBeforeLaunch = new Project(project);
|
||||
EnterSnapshotMode(true);
|
||||
}
|
||||
|
||||
private void SendCreateTelemetryEvent(Project project)
|
||||
{
|
||||
var telemetryEvent = new CreateEvent();
|
||||
telemetryEvent.Successful = true;
|
||||
telemetryEvent.NumScreens = project.Monitors.Count;
|
||||
telemetryEvent.AppCount = project.Applications.Count;
|
||||
telemetryEvent.CliCount = project.Applications.FindAll(app => app.CommandLineArguments.Length > 0).Count;
|
||||
telemetryEvent.ShortcutCreated = project.IsShortcutNeeded;
|
||||
telemetryEvent.AdminCount = project.Applications.FindAll(app => app.IsElevated).Count;
|
||||
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
|
||||
}
|
||||
|
||||
private void SendEditTelemetryEvent(Project updatedProject, Project prevProject)
|
||||
{
|
||||
int appsRemovedCount = updatedProject.Applications.FindAll(val => !val.IsIncluded).Count;
|
||||
foreach (var app in prevProject.Applications)
|
||||
{
|
||||
var updatedApp = updatedProject.Applications.Find(val => app.AppName == val.AppName && app.Position == val.Position);
|
||||
if (updatedApp == null)
|
||||
{
|
||||
appsRemovedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
int appsAddedCount = 0;
|
||||
int cliAdded = 0, cliRemoved = 0;
|
||||
int adminAdded = 0, adminRemoved = 0;
|
||||
foreach (var app in updatedProject.Applications)
|
||||
{
|
||||
var prevApp = prevProject.Applications.Find(val => app.AppName == val.AppName && app.Position == val.Position);
|
||||
if (prevApp == null)
|
||||
{
|
||||
if (app.IsIncluded)
|
||||
{
|
||||
appsAddedCount++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (app.CommandLineArguments.Length > 0 && prevApp.CommandLineArguments.Length == 0)
|
||||
{
|
||||
cliAdded++;
|
||||
}
|
||||
|
||||
if (prevApp.CommandLineArguments.Length > 0 && app.CommandLineArguments.Length == 0)
|
||||
{
|
||||
cliRemoved++;
|
||||
}
|
||||
|
||||
if (app.IsElevated && !prevApp.IsElevated)
|
||||
{
|
||||
adminAdded++;
|
||||
}
|
||||
|
||||
if (!app.IsElevated && prevApp.IsElevated)
|
||||
{
|
||||
adminRemoved++;
|
||||
}
|
||||
}
|
||||
|
||||
var telemetryEvent = new EditEvent();
|
||||
telemetryEvent.Successful = true;
|
||||
telemetryEvent.ScreenCountDelta = updatedProject.Monitors.Count - prevProject.Monitors.Count;
|
||||
telemetryEvent.AppsAdded = appsAddedCount;
|
||||
telemetryEvent.AppsRemoved = appsRemovedCount;
|
||||
telemetryEvent.CliAdded = cliAdded;
|
||||
telemetryEvent.CliRemoved = cliRemoved;
|
||||
telemetryEvent.AdminAdded = adminAdded;
|
||||
telemetryEvent.AdminRemoved = adminRemoved;
|
||||
telemetryEvent.PixelAdjustmentsUsed = updatedProject.IsPositionChangedManually;
|
||||
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
|
||||
}
|
||||
|
||||
private void SendDeleteTelemetryEvent()
|
||||
{
|
||||
var telemetryEvent = new EditEvent();
|
||||
telemetryEvent.Successful = true;
|
||||
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/modules/Workspaces/WorkspacesEditor/WorkspacesEditor.csproj
Normal file
106
src/modules/Workspaces/WorkspacesEditor/WorkspacesEditor.csproj
Normal file
@@ -0,0 +1,106 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\Common.SelfContained.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>PowerToys.WorkspacesEditor</AssemblyTitle>
|
||||
<AssemblyDescription>PowerToys Workspaces Editor</AssemblyDescription>
|
||||
<Description>PowerToys Workspaces Editor</Description>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{367D7543-7DBA-4381-99F1-BF6142A996C4}</ProjectGuid>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>images\Workspaces.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<AssemblyName>PowerToys.WorkspacesEditor</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="images\DefaultIcon.ico" />
|
||||
<None Remove="images\Workspaces.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="IWshRuntimeLibrary">
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<Guid>f935dc20-1cf0-11d0-adb9-00c04fd58a0b</Guid>
|
||||
<Lcid>0</Lcid>
|
||||
<Isolated>false</Isolated>
|
||||
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="Shell32">
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<Guid>50a7e9b0-70ef-11d1-b75a-00a0c90564fe</Guid>
|
||||
<Lcid>0</Lcid>
|
||||
<Isolated>false</Isolated>
|
||||
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="images\DefaultIcon.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="images\Workspaces.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="app.manifest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ControlzEx" />
|
||||
<PackageReference Include="ModernWpfUI" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\GPOWrapperProjection\GPOWrapperProjection.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<_DeploymentManifestIconFile Remove="images\Workspaces.ico" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,420 @@
|
||||
<Page
|
||||
x:Class="WorkspacesEditor.ProjectEditor"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:WorkspacesEditor.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:WorkspacesEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="clr-namespace:WorkspacesEditor.Models"
|
||||
xmlns:props="clr-namespace:WorkspacesEditor.Properties"
|
||||
Title="Workspaces Editor"
|
||||
Background="{DynamicResource PrimaryBackgroundBrush}"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BoolToVis" />
|
||||
|
||||
<Style x:Key="TextBlockEnabledStyle" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryForegroundBrush}" />
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource SecondaryForegroundBrush}" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style x:Key="ButtonEnabledStyle" TargetType="Button">
|
||||
<Setter Property="Foreground" Value="{DynamicResource PrimaryForegroundBrush}" />
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{DynamicResource SecondaryForegroundBrush}" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="headerTemplate">
|
||||
<Border HorizontalAlignment="Stretch">
|
||||
<TextBlock
|
||||
Margin="0,0,20,0"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{Binding MonitorName}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="appTemplate">
|
||||
<Border
|
||||
Margin="1"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
MouseEnter="AppBorder_MouseEnter"
|
||||
MouseLeave="AppBorder_MouseLeave">
|
||||
<Expander
|
||||
Margin="0,0,20,0"
|
||||
FlowDirection="RightToLeft"
|
||||
IsEnabled="{Binding IsIncluded, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsExpanded="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||
<Expander.Header>
|
||||
<Grid HorizontalAlignment="{Binding HorizontalAlignment, RelativeSource={RelativeSource AncestorType=ContentPresenter}, Mode=OneWayToSource}" FlowDirection="LeftToRight">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="60" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="5,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="#EED202"
|
||||
Text=""
|
||||
ToolTip="{x:Static props:Resources.NotFoundTooltip}"
|
||||
Visibility="{Binding IsNotFound, Converter={StaticResource BoolToVis}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Image
|
||||
Grid.Column="1"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Source="{Binding IconBitmapImage}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Width="20"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{Binding RepeatIndexString, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<StackPanel Grid.Column="3" VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{Binding AppName}" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource SecondaryForegroundBrush}"
|
||||
Text="{Binding AppMainParams, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Visibility="{Binding IsAppMainParamVisible, Converter={StaticResource BoolToVis}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
<controls:ResetIsEnabled Grid.Column="4">
|
||||
<Button
|
||||
Width="120"
|
||||
Margin="10,5"
|
||||
Padding="24,6"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Delete}"
|
||||
Background="{DynamicResource TertiaryBackgroundBrush}"
|
||||
Click="DeleteButtonClicked"
|
||||
Content="{Binding DeleteButtonContent, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="True" />
|
||||
</controls:ResetIsEnabled>
|
||||
</Grid>
|
||||
</Expander.Header>
|
||||
<Grid
|
||||
Margin="-20,0,0,0"
|
||||
HorizontalAlignment="{Binding HorizontalAlignment, RelativeSource={RelativeSource AncestorType=ContentPresenter}, Mode=OneWayToSource}"
|
||||
Background="{DynamicResource QuaternaryBackgroundBrush}"
|
||||
FlowDirection="LeftToRight">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<DockPanel Margin="100,5,0,0">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.CliArguments}" />
|
||||
<TextBox
|
||||
x:Name="CommandLineTextBox"
|
||||
Margin="15,0,50,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
Background="{DynamicResource TertiaryBackgroundBrush}"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{Binding CommandLineArguments, Mode=TwoWay}"
|
||||
TextChanged="CommandLineTextBox_TextChanged" />
|
||||
</DockPanel>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="100,5,0,0"
|
||||
Orientation="Horizontal">
|
||||
<CheckBox
|
||||
MinWidth="10"
|
||||
Content="{x:Static props:Resources.LaunchAsAdmin}"
|
||||
IsChecked="{Binding IsElevated, Mode=TwoWay}"
|
||||
IsEnabled="{Binding CanLaunchElevated, Mode=OneWay}" />
|
||||
<CheckBox
|
||||
MinWidth="10"
|
||||
Margin="15,0,0,0"
|
||||
Checked="MaximizedChecked"
|
||||
Content="{x:Static props:Resources.Maximized}"
|
||||
IsChecked="{Binding Maximized, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<CheckBox
|
||||
MinWidth="10"
|
||||
Margin="15,0,0,0"
|
||||
Checked="MinimizedChecked"
|
||||
Content="{x:Static props:Resources.Minimized}"
|
||||
IsChecked="{Binding Minimized, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Margin="100,5,0,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsEnabled="{Binding EditPositionEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource TextBlockEnabledStyle}"
|
||||
Text="{x:Static props:Resources.Left}" />
|
||||
<TextBox
|
||||
x:Name="LeftTextBox"
|
||||
Margin="15,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
Background="{DynamicResource TertiaryBackgroundBrush}"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsEnabled="{Binding EditPositionEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Text="{Binding Position.X, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
TextChanged="LeftTextBox_TextChanged" />
|
||||
<TextBlock
|
||||
Margin="15,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsEnabled="{Binding EditPositionEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource TextBlockEnabledStyle}"
|
||||
Text="{x:Static props:Resources.Top}" />
|
||||
<TextBox
|
||||
x:Name="TopTextBox"
|
||||
Margin="15,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
Background="{DynamicResource TertiaryBackgroundBrush}"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsEnabled="{Binding EditPositionEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Text="{Binding Position.Y, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
TextChanged="TopTextBox_TextChanged" />
|
||||
<TextBlock
|
||||
Margin="15,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsEnabled="{Binding EditPositionEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource TextBlockEnabledStyle}"
|
||||
Text="{x:Static props:Resources.Width}" />
|
||||
<TextBox
|
||||
x:Name="WidthTextBox"
|
||||
Margin="15,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
Background="{DynamicResource TertiaryBackgroundBrush}"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsEnabled="{Binding EditPositionEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Text="{Binding Position.Width, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
TextChanged="WidthTextBox_TextChanged" />
|
||||
<TextBlock
|
||||
Margin="15,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsEnabled="{Binding EditPositionEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource TextBlockEnabledStyle}"
|
||||
Text="{x:Static props:Resources.Height}" />
|
||||
<TextBox
|
||||
x:Name="HeightTextBox"
|
||||
Margin="15,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
Background="{DynamicResource TertiaryBackgroundBrush}"
|
||||
BorderThickness="0"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsEnabled="{Binding EditPositionEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Text="{Binding Position.Height, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
TextChanged="HeightTextBox_TextChanged" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Expander>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
<models:AppListDataTemplateSelector
|
||||
x:Key="AppListDataTemplateSelector"
|
||||
AppTemplate="{StaticResource appTemplate}"
|
||||
HeaderTemplate="{StaticResource headerTemplate}" />
|
||||
</Page.Resources>
|
||||
<Grid Margin="40,0,40,40">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<Button
|
||||
Margin="0,20,0,20"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
Click="CancelButtonClicked">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="24"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.Workspaces}" />
|
||||
</Button>
|
||||
<TextBlock
|
||||
Margin="10,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{DynamicResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="" />
|
||||
<TextBlock
|
||||
Margin="10,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{Binding EditorWindowTitle}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="1" Orientation="Vertical">
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.WorkspaceName}" />
|
||||
<TextBox
|
||||
x:Name="EditNameTextBox"
|
||||
Width="320"
|
||||
Margin="0,6,0,6"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PrimaryBorderBrush}"
|
||||
BorderThickness="2"
|
||||
GotFocus="EditNameTextBox_GotFocus"
|
||||
KeyDown="EditNameTextBoxKeyDown"
|
||||
Text="{Binding Name, Mode=TwoWay}"
|
||||
TextChanged="EditNameTextBox_TextChanged" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{DynamicResource MonitorViewBackgroundBrush}"
|
||||
CornerRadius="5">
|
||||
<DockPanel>
|
||||
<Image
|
||||
Width="{Binding PreviewImageWidth, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Height="200"
|
||||
Margin="2"
|
||||
DockPanel.Dock="Top"
|
||||
Source="{Binding PreviewImage, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Stretch="Fill" />
|
||||
<Button
|
||||
x:Name="RevertButton"
|
||||
Height="36"
|
||||
Margin="0,0,20,10"
|
||||
Padding="24,0,24,0"
|
||||
HorizontalAlignment="Right"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Revert}"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
Click="RevertButtonClicked"
|
||||
Content="{x:Static props:Resources.Revert}"
|
||||
DockPanel.Dock="Right"
|
||||
IsEnabled="{Binding IsRevertEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Button
|
||||
x:Name="LaunchEditButton"
|
||||
Height="36"
|
||||
Margin="0,0,10,10"
|
||||
Padding="24,0,24,0"
|
||||
HorizontalAlignment="Right"
|
||||
AutomationProperties.Name="{x:Static props:Resources.LaunchEdit}"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
Click="LaunchEditButtonClicked"
|
||||
Content="{x:Static props:Resources.LaunchEdit}"
|
||||
DockPanel.Dock="Right" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<ScrollViewer
|
||||
Grid.Row="4"
|
||||
Margin="0,10,0,0"
|
||||
PreviewMouseWheel="ScrollViewer_PreviewMouseWheel"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<ItemsControl ItemTemplateSelector="{StaticResource AppListDataTemplateSelector}" ItemsSource="{Binding ApplicationsListed, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<StackPanel
|
||||
Grid.Row="5"
|
||||
Margin="0,5,0,0"
|
||||
Orientation="Horizontal"
|
||||
Visibility="Collapsed">
|
||||
<CheckBox
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static props:Resources.MoveIfExist}"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
IsChecked="{Binding MoveExistingWindows, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
<DockPanel Grid.Row="6" Margin="0,20,0,20">
|
||||
<CheckBox
|
||||
Content="{x:Static props:Resources.CreateShortcut}"
|
||||
DockPanel.Dock="Left"
|
||||
FontSize="14"
|
||||
IsChecked="{Binding IsShortcutNeeded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<StackPanel
|
||||
Margin="40,0,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Right"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="CancelButton"
|
||||
Height="36"
|
||||
Margin="20,0,0,0"
|
||||
Padding="24,0,24,0"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Cancel}"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
Click="CancelButtonClicked"
|
||||
Content="{x:Static props:Resources.Cancel}" />
|
||||
<Button
|
||||
x:Name="SaveButton"
|
||||
Height="36"
|
||||
Margin="20,0,0,0"
|
||||
Padding="24,0,24,0"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Save_Workspace}"
|
||||
Click="SaveButtonClicked"
|
||||
Content="{x:Static props:Resources.Save_Workspace}"
|
||||
IsEnabled="{Binding CanBeSaved, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,221 @@
|
||||
// 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 System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using WorkspacesEditor.Data;
|
||||
using WorkspacesEditor.Models;
|
||||
using WorkspacesEditor.ViewModels;
|
||||
|
||||
namespace WorkspacesEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ProjectEditor.xaml
|
||||
/// </summary>
|
||||
public partial class ProjectEditor : Page
|
||||
{
|
||||
private const double ScrollSpeed = 15;
|
||||
private MainViewModel _mainViewModel;
|
||||
|
||||
public ProjectEditor(MainViewModel mainViewModel)
|
||||
{
|
||||
_mainViewModel = mainViewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void SaveButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Project projectToSave = this.DataContext as Project;
|
||||
projectToSave.CloseExpanders();
|
||||
|
||||
if (_mainViewModel.Workspaces.Any(x => x.Id == projectToSave.Id))
|
||||
{
|
||||
_mainViewModel.SaveProject(projectToSave);
|
||||
}
|
||||
else
|
||||
{
|
||||
_mainViewModel.AddNewProject(projectToSave);
|
||||
}
|
||||
|
||||
_mainViewModel.SwitchToMainView();
|
||||
}
|
||||
|
||||
private void CancelButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// delete the temp file created by the snapshot tool
|
||||
TempProjectData.DeleteTempFile();
|
||||
|
||||
_mainViewModel.SwitchToMainView();
|
||||
}
|
||||
|
||||
private void DeleteButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Button button = sender as Button;
|
||||
Models.Application app = button.DataContext as Models.Application;
|
||||
app.SwitchDeletion();
|
||||
}
|
||||
|
||||
private void EditNameTextBoxKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
{
|
||||
e.Handled = true;
|
||||
Project project = this.DataContext as Project;
|
||||
TextBox textBox = sender as TextBox;
|
||||
project.Name = textBox.Text;
|
||||
}
|
||||
else if (e.Key == Key.Escape)
|
||||
{
|
||||
e.Handled = true;
|
||||
Project project = this.DataContext as Project;
|
||||
_mainViewModel.CancelProjectName(project);
|
||||
}
|
||||
}
|
||||
|
||||
private void EditNameTextBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_mainViewModel.SaveProjectName(DataContext as Project);
|
||||
}
|
||||
|
||||
private void AppBorder_MouseEnter(object sender, MouseEventArgs e)
|
||||
{
|
||||
Border border = sender as Border;
|
||||
Models.Application app = border.DataContext as Models.Application;
|
||||
app.IsHighlighted = true;
|
||||
Project project = app.Parent;
|
||||
project.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
|
||||
private void AppBorder_MouseLeave(object sender, MouseEventArgs e)
|
||||
{
|
||||
Border border = sender as Border;
|
||||
Models.Application app = border.DataContext as Models.Application;
|
||||
if (app == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
app.IsHighlighted = false;
|
||||
Project project = app.Parent;
|
||||
project.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
|
||||
private void EditNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
Project project = this.DataContext as Project;
|
||||
TextBox textBox = sender as TextBox;
|
||||
project.Name = textBox.Text;
|
||||
project.OnPropertyChanged(new PropertyChangedEventArgs(nameof(Project.CanBeSaved)));
|
||||
}
|
||||
|
||||
private void LeftTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
TextBox textBox = sender as TextBox;
|
||||
Models.Application application = textBox.DataContext as Models.Application;
|
||||
int newPos;
|
||||
if (!int.TryParse(textBox.Text, out newPos))
|
||||
{
|
||||
newPos = 0;
|
||||
}
|
||||
|
||||
application.Position = new Models.Application.WindowPosition() { X = newPos, Y = application.Position.Y, Width = application.Position.Width, Height = application.Position.Height };
|
||||
Project project = application.Parent;
|
||||
project.IsPositionChangedManually = true;
|
||||
project.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
|
||||
private void TopTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
TextBox textBox = sender as TextBox;
|
||||
Models.Application application = textBox.DataContext as Models.Application;
|
||||
int newPos;
|
||||
if (!int.TryParse(textBox.Text, out newPos))
|
||||
{
|
||||
newPos = 0;
|
||||
}
|
||||
|
||||
application.Position = new Models.Application.WindowPosition() { X = application.Position.X, Y = newPos, Width = application.Position.Width, Height = application.Position.Height };
|
||||
Project project = application.Parent;
|
||||
project.IsPositionChangedManually = true;
|
||||
project.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
|
||||
private void WidthTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
TextBox textBox = sender as TextBox;
|
||||
Models.Application application = textBox.DataContext as Models.Application;
|
||||
int newPos;
|
||||
if (!int.TryParse(textBox.Text, out newPos))
|
||||
{
|
||||
newPos = 0;
|
||||
}
|
||||
|
||||
application.Position = new Models.Application.WindowPosition() { X = application.Position.X, Y = application.Position.Y, Width = newPos, Height = application.Position.Height };
|
||||
Project project = application.Parent;
|
||||
project.IsPositionChangedManually = true;
|
||||
project.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
|
||||
private void HeightTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
TextBox textBox = sender as TextBox;
|
||||
Models.Application application = textBox.DataContext as Models.Application;
|
||||
int newPos;
|
||||
if (!int.TryParse(textBox.Text, out newPos))
|
||||
{
|
||||
newPos = 0;
|
||||
}
|
||||
|
||||
application.Position = new Models.Application.WindowPosition() { X = application.Position.X, Y = application.Position.Y, Width = application.Position.Width, Height = newPos };
|
||||
Project project = application.Parent;
|
||||
project.IsPositionChangedManually = true;
|
||||
project.Initialize(App.ThemeManager.GetCurrentTheme());
|
||||
}
|
||||
|
||||
private void CommandLineTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
TextBox textBox = sender as TextBox;
|
||||
Models.Application application = textBox.DataContext as Models.Application;
|
||||
application.CommandLineTextChanged(textBox.Text);
|
||||
}
|
||||
|
||||
private void MaximizedChecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CheckBox checkBox = sender as CheckBox;
|
||||
Models.Application application = checkBox.DataContext as Models.Application;
|
||||
application.MaximizedChecked();
|
||||
}
|
||||
|
||||
private void MinimizedChecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CheckBox checkBox = sender as CheckBox;
|
||||
Models.Application application = checkBox.DataContext as Models.Application;
|
||||
application.MinimizedChecked();
|
||||
}
|
||||
|
||||
private void LaunchEditButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Button button = sender as Button;
|
||||
Project project = button.DataContext as Project;
|
||||
_mainViewModel.LaunchAndEdit(project);
|
||||
}
|
||||
|
||||
private void RevertButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_mainViewModel.RevertLaunch();
|
||||
}
|
||||
|
||||
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
ScrollViewer scrollViewer = sender as ScrollViewer;
|
||||
double scrollAmount = Math.Sign(e.Delta) * ScrollSpeed;
|
||||
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - scrollAmount);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
74
src/modules/Workspaces/WorkspacesEditor/app.manifest
Normal file
74
src/modules/Workspaces/WorkspacesEditor/app.manifest
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC Manifest Options
|
||||
If you want to change the Windows User Account Control level replace the
|
||||
requestedExecutionLevel node with one of the following.
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||
Remove this element if your application requires this virtualization for backwards
|
||||
compatibility.
|
||||
-->
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- A list of the Windows versions that this application has been tested on
|
||||
and is designed to work with. Uncomment the appropriate elements
|
||||
and Windows will automatically select the most compatible environment. -->
|
||||
|
||||
<!-- Windows Vista -->
|
||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||
|
||||
<!-- Windows 7 -->
|
||||
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||
|
||||
<!-- Windows 8 -->
|
||||
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||
|
||||
<!-- Windows 8.1 -->
|
||||
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
|
||||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||
<!--
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
</assembly>
|
||||
BIN
src/modules/Workspaces/WorkspacesEditor/images/DefaultIcon.ico
Normal file
BIN
src/modules/Workspaces/WorkspacesEditor/images/DefaultIcon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
BIN
src/modules/Workspaces/WorkspacesEditor/images/Workspaces.ico
Normal file
BIN
src/modules/Workspaces/WorkspacesEditor/images/Workspaces.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 456 KiB |
Reference in New Issue
Block a user