Merge branch 'master' of https://github.com/microsoft/PowerToys into microsoft-master

This commit is contained in:
Den Delimarsky
2021-04-07 17:12:51 -07:00
610 changed files with 21033 additions and 18367 deletions

View File

@@ -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 ManagedCommon
{
public enum StartupPosition
{
Cursor,
PrimaryMonitor,
Focus,
}
}

View File

@@ -16,6 +16,8 @@ namespace Microsoft.PowerToys.Telemetry.Events
{
public bool UTCReplace_AppSessionGuid => true;
public string EventName { get; set; }
private string _version;
public string Version

View File

@@ -37,7 +37,7 @@ namespace Microsoft.PowerToys.Telemetry
where T : EventBase, IEvent
{
this.Write<T>(
null,
telemetryEvent.EventName,
new EventSourceOptions()
{
Keywords = ProjectKeywordMeasure,

View File

@@ -4,7 +4,6 @@
namespace PTSettingsHelper
{
constexpr inline const wchar_t* settings_filename = L"\\settings.json";
constexpr inline const wchar_t* log_settings_filename = L"log_settings.json";
constexpr inline const wchar_t* oobe_filename = L"oobe_settings.json";
std::wstring get_root_save_folder_location()

View File

@@ -6,6 +6,8 @@
namespace PTSettingsHelper
{
constexpr inline const wchar_t* log_settings_filename = L"log_settings.json";
std::wstring get_module_save_folder_location(std::wstring_view powertoy_name);
std::wstring get_root_save_folder_location();

View File

@@ -52,10 +52,13 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
<PackageReference Include="coverlet.collector" Version="3.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@@ -135,10 +135,24 @@ public
public:
literal int VK_WIN_BOTH = CommonSharedConstants::VK_WIN_BOTH;
static String ^ AppDataPath() {
auto localPath = Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData);
auto powerToysPath = gcnew String(CommonSharedConstants::APPDATA_PATH);
return System::IO::Path::Combine(localPath, powerToysPath);
}
static String ^ PowerLauncherSharedEvent() {
return gcnew String(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT);
}
static String ^ RunSendSettingsTelemetryEvent() {
return gcnew String(CommonSharedConstants::RUN_SEND_SETTINGS_TELEMETRY_EVENT);
}
static String ^ ColorPickerSendSettingsTelemetryEvent() {
return gcnew String(CommonSharedConstants::COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT);
}
static String ^ ShowColorPickerSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_COLOR_PICKER_SHARED_EVENT);
}

View File

@@ -10,9 +10,15 @@ namespace CommonSharedConstants
// Fake key code to represent VK_WIN.
inline const int VK_WIN_BOTH = 0x104;
const wchar_t APPDATA_PATH[] = L"Microsoft\\PowerToys";
// Path to the event used by PowerLauncher
const wchar_t POWER_LAUNCHER_SHARED_EVENT[] = L"Local\\PowerToysRunInvokeEvent-30f26ad7-d36d-4c0e-ab02-68bb5ff3c4ab";
const wchar_t RUN_SEND_SETTINGS_TELEMETRY_EVENT[] = L"Local\\PowerToysRunInvokeEvent-638ec522-0018-4b96-837d-6bd88e06f0d6";
const wchar_t COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT[] = L"Local\\ColorPickerSettingsTelemetryEvent-6c7071d8-4014-46ec-b687-913bd8a422f1";
// Path to the event used to show Color Picker
const wchar_t SHOW_COLOR_PICKER_SHARED_EVENT[] = L"Local\\ShowColorPickerEvent-8c46be2a-3e05-4186-b56b-4ae986ef2525";

View File

@@ -38,7 +38,7 @@ level_enum getLogLevel(std::wstring_view logSettingsPath)
return result;
}
std::shared_ptr<spdlog::logger> Logger::logger;
std::shared_ptr<spdlog::logger> Logger::logger = spdlog::null_logger_mt("null");
bool Logger::wasLogFailedShown()
{

View File

@@ -4,16 +4,18 @@
struct LogSettings
{
// The following strings are not localizable
inline const static std::wstring defaultLogLevel = L"warn";
inline const static std::wstring defaultLogLevel = L"trace";
inline const static std::wstring logLevelOption = L"logLevel";
inline const static std::string runnerLoggerName = "runner";
inline const static std::wstring logPath = L"Logs\\";
inline const static std::wstring runnerLogPath = L"RunnerLogs\\runner-log.txt";
inline const static std::string actionRunnerLoggerName = "action-runner";
inline const static std::wstring actionRunnerLogPath = L"RunnerLogs\\action-runner-log.txt";
inline const static std::string launcherLoggerName = "launcher";
inline const static std::wstring launcherLogPath = L"LogsModuleInterface\\launcher-log.txt";
inline const static std::string fancyZonesLoggerName = "fancyzones";
inline const static std::wstring fancyZonesLogPath = L"FancyZonesLogs\\fancyzones-log.txt";
inline const static std::wstring fancyZonesLogPath = L"fancyzones-log.txt";
inline const static std::wstring fancyZonesOldLogPath = L"FancyZonesLogs\\"; // needed to clean up old logs
inline const static std::string shortcutGuideLoggerName = "shortcut-guide";
inline const static std::wstring shortcutGuideLogPath = L"ShortcutGuideLogs\\shortcut-guide-log.txt";
inline const static std::string keyboardManagerLoggerName = "keyboard-manager";

View File

@@ -10,7 +10,7 @@ namespace fs = std::filesystem;
namespace updating
{
constexpr size_t REQUIRED_MINIMAL_PATCH = 11;
constexpr size_t REQUIRED_MINIMAL_PATCH = 13;
bool dotnet_is_installed()
{
@@ -46,7 +46,7 @@ namespace updating
std::optional<fs::path> download_dotnet()
{
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/3f1cc4f7-0c1a-48ca-9551-a8447fa55892/ed9809822448f55b649858920afb35cb/windowsdesktop-runtime-3.1.11-win-x64.exe";
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/aa717f57-3ae5-48fa-a3ab-0018338d0726/fb37276b1575772461701339110e7a54/windowsdesktop-runtime-3.1.13-win-x64.exe";
const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime.exe";
auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME;

View File

@@ -3,6 +3,7 @@
#include "installer.h"
#include <common/version/version.h>
#include <common/notifications/notifications.h>
#include <common/utils/os-detect.h>
#include "utils/winapi_error.h"
namespace // Strings in this namespace should not be localized
@@ -192,4 +193,8 @@ namespace updating
co_return false;
}
bool is_old_windows_version()
{
return !Is19H1OrHigher();
}
}

View File

@@ -16,4 +16,6 @@ namespace updating
std::optional<VersionHelper> get_installed_powertoys_version();
std::future<bool> uninstall_previous_msix_version_async();
bool is_old_windows_version();
}

View File

@@ -7,9 +7,10 @@
#include "notifications.h"
#include "updating.h"
#include <common/utils/json.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/notifications/notifications.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/json.h>
#include <common/utils/os-detect.h>
namespace // Strings in this namespace should not be localized
{
@@ -68,12 +69,17 @@ namespace updating
{
co_return nonstd::make_unexpected(strings.GITHUB_NEW_VERSION_USING_LOCAL_BUILD_ERROR);
}
try
{
http::HttpClient client;
json::JsonObject release_object;
const VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
VersionHelper github_version = current_version;
// On a <1903 system, block updates to 0.36+
const bool blockNonPatchReleases = current_version.major == 0 && current_version.minor == 35 && !Is19H1OrHigher();
if (prerelease)
{
const auto body = co_await client.request(Uri{ ALL_RELEASES_ENDPOINT });
@@ -102,6 +108,11 @@ namespace updating
}
}
if (blockNonPatchReleases && github_version >= VersionHelper{ 0, 36, 0 })
{
co_return version_up_to_date{};
}
if (github_version <= current_version)
{
co_return version_up_to_date{};

View File

@@ -0,0 +1,55 @@
#pragma once
#include <filesystem>
#include <common/version/version.h>
namespace LoggerHelpers
{
inline std::filesystem::path get_log_folder_path(std::wstring_view appPath)
{
std::filesystem::path logFolderPath(appPath);
logFolderPath.append(LogSettings::logPath);
logFolderPath.append(get_product_version());
return logFolderPath;
}
inline bool delete_old_log_folder(const std::filesystem::path& logFolderPath)
{
try
{
std::filesystem::remove_all(logFolderPath);
return true;
}
catch (std::filesystem::filesystem_error& e)
{
Logger::error("Failed to delete old log folder: {}", e.what());
}
return false;
}
inline bool delete_other_versions_log_folders(std::wstring_view appPath, const std::filesystem::path& currentVersionLogFolder)
{
bool result = true;
std::filesystem::path logFolderPath(appPath);
logFolderPath.append(LogSettings::logPath);
for (const auto& dir : std::filesystem::directory_iterator(logFolderPath))
{
if (dir != currentVersionLogFolder)
{
try
{
std::filesystem::remove_all(dir);
}
catch (std::filesystem::filesystem_error& e)
{
Logger::error("Failed to delete previous version log folder: {}", e.what());
result = false;
}
}
}
return result;
}
}

View File

@@ -2,7 +2,7 @@
#include <winrt/Windows.Foundation.Metadata.h>
// The following three helper functions determine if the user has a build version higher than or equal to 19h1, as that is a requirement for xaml islands
// The following three helper functions determine if the user has a build version higher than or equal to 19h1 (aka 1903), as that is a requirement for xaml islands
// Source : Microsoft-ui-xaml github
// Link: https://github.com/microsoft/microsoft-ui-xaml/blob/c045cde57c5c754683d674634a0baccda34d58c4/dev/dll/SharedHelpers.cpp
template<uint16_t APIVersion>

View File

@@ -39,3 +39,14 @@ std::wstring VersionHelper::toWstring() const
result += std::to_wstring(revision);
return result;
}
std::string VersionHelper::toString() const
{
std::string result{ "v" };
result += std::to_string(major);
result += '.';
result += std::to_string(minor);
result += '.';
result += std::to_string(revision);
return result;
}

View File

@@ -15,4 +15,5 @@ struct VersionHelper
size_t revision;
std::wstring toWstring() const;
std::string toString() const;
};

View File

@@ -54,6 +54,9 @@
<ClCompile Include="trace.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
@@ -69,6 +72,7 @@
<None Include="Resources.resx" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>

View File

@@ -4,11 +4,13 @@
#include <interface/powertoy_module_interface.h>
#include "trace.h"
#include "Generated Files/resource.h"
#include <common/logger/logger.h>
#include <common/SettingsAPI/settings_objects.h>
#include <common/utils/os-detect.h>
#include <common/utils/resources.h>
#include <colorPicker/ColorPicker/ColorPickerConstants.h>
#include <common/interop/shared_constants.h>
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
@@ -28,6 +30,17 @@ BOOL APIENTRY DllMain(HMODULE hModule,
return TRUE;
}
namespace
{
const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
const wchar_t JSON_KEY_WIN[] = L"win";
const wchar_t JSON_KEY_ALT[] = L"alt";
const wchar_t JSON_KEY_CTRL[] = L"ctrl";
const wchar_t JSON_KEY_SHIFT[] = L"shift";
const wchar_t JSON_KEY_CODE[] = L"code";
const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut";
}
struct ModuleSettings
{
} g_settings;
@@ -47,11 +60,103 @@ private:
// Time to wait for process to close after sending WM_CLOSE signal
static const int MAX_WAIT_MILLISEC = 10000;
HANDLE send_telemetry_event;
Hotkey m_hotkey;
// Handle to event used to invoke ColorPicker
HANDLE m_hInvokeEvent;
void parse_hotkey(PowerToysSettings::PowerToyValues& settings)
{
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size())
{
try
{
auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_SHORTCUT);
m_hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
m_hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
}
catch (...)
{
Logger::error("Failed to initialize ColorPicker start shortcut");
}
}
else
{
Logger::info("ColorPicker settings are empty");
}
if (!m_hotkey.key)
{
Logger::info("ColorPicker is going to use default shortcut");
m_hotkey.win = true;
m_hotkey.alt = false;
m_hotkey.shift = true;
m_hotkey.ctrl = false;
m_hotkey.key = 'C';
}
}
bool is_process_running()
{
return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT;
}
void launch_process()
{
Logger::trace(L"Launching ColorPicker process");
unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring executable_args = L"";
executable_args.append(std::to_wstring(powertoys_pid));
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = L"modules\\ColorPicker\\ColorPickerUI.exe";
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = executable_args.data();
if (!ShellExecuteExW(&sei))
{
DWORD error = GetLastError();
std::wstring message = L"ColorPicker failed to start with error = ";
message += std::to_wstring(error);
Logger::error(message);
}
m_hProcess = sei.hProcess;
}
// Load the settings file.
void init_settings()
{
try
{
// Load and parse the settings file for this PowerToy.
PowerToysSettings::PowerToyValues settings =
PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
parse_hotkey(settings);
}
catch (std::exception ex)
{
Logger::warn(L"An exception occurred while loading the settings file");
// Error while loading from the settings file. Let default values stay as they are.
}
}
public:
ColorPicker()
{
app_name = GET_RESOURCE_STRING(IDS_COLORPICKER_NAME);
app_key = ColorPickerConstants::ModuleKey;
send_telemetry_event = CreateDefaultEvent(CommonSharedConstants::COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT);
m_hInvokeEvent = CreateDefaultEvent(CommonSharedConstants::SHOW_COLOR_PICKER_SHARED_EVENT);
init_settings();
}
~ColorPicker()
@@ -105,6 +210,7 @@ public:
PowerToysSettings::PowerToyValues values =
PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
parse_hotkey(values);
// If you don't need to do any custom processing of the settings, proceed
// to persists the values calling:
values.save_to_settings_file();
@@ -119,23 +225,12 @@ public:
virtual void enable()
{
ResetEvent(send_telemetry_event);
ResetEvent(m_hInvokeEvent);
// use only with new settings?
if (UseNewSettings())
{
unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring executable_args = L"";
executable_args.append(std::to_wstring(powertoys_pid));
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = L"modules\\ColorPicker\\ColorPickerUI.exe";
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = executable_args.data();
ShellExecuteExW(&sei);
m_hProcess = sei.hProcess;
launch_process();
m_enabled = true;
}
};
@@ -144,16 +239,57 @@ public:
{
if (m_enabled)
{
ResetEvent(send_telemetry_event);
ResetEvent(m_hInvokeEvent);
TerminateProcess(m_hProcess, 1);
}
m_enabled = false;
}
virtual bool on_hotkey(size_t hotkeyId) override
{
if (m_enabled)
{
Logger::trace(L"ColorPicker hotkey pressed");
if (!is_process_running())
{
launch_process();
}
SetEvent(m_hInvokeEvent);
return true;
}
return false;
}
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
{
if (m_hotkey.key)
{
if (hotkeys && buffer_size >= 1)
{
hotkeys[0] = m_hotkey;
}
return 1;
}
else
{
return 0;
}
}
virtual bool is_enabled() override
{
return m_enabled;
}
virtual void send_settings_telemetry() override
{
SetEvent(send_telemetry_event);
}
};
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()

View File

@@ -18,7 +18,7 @@ namespace ColorPickerUI
{
private Mutex _instanceMutex;
private static string[] _args;
private int _powerToysPid;
private int _powerToysRunnerPid;
private bool disposedValue;
private ThemeManager _themeManager;
@@ -27,23 +27,27 @@ namespace ColorPickerUI
_args = e?.Args;
// allow only one instance of color picker
_instanceMutex = new Mutex(true, @"Global\ColorPicker", out bool createdNew);
_instanceMutex = new Mutex(true, @"Local\PowerToys_ColorPicker_InstanceMutex", out bool createdNew);
if (!createdNew)
{
_instanceMutex = null;
Application.Current.Shutdown();
Environment.Exit(0);
return;
}
if (_args?.Length > 0)
{
_ = int.TryParse(_args[0], out _powerToysPid);
}
_ = int.TryParse(_args[0], out _powerToysRunnerPid);
RunnerHelper.WaitForPowerToysRunner(_powerToysPid, () =>
RunnerHelper.WaitForPowerToysRunner(_powerToysRunnerPid, () =>
{
Environment.Exit(0);
});
}
else
{
Environment.Exit(0);
});
_powerToysRunnerPid = -1;
}
_themeManager = new ThemeManager(this);
base.OnStartup(e);
@@ -83,5 +87,10 @@ namespace ColorPickerUI
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
public bool IsRunningDetachedFromPowerToys()
{
return _powerToysRunnerPid == -1;
}
}
}

View File

@@ -2,19 +2,8 @@
// 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.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using ColorPicker.Helpers;
namespace ColorPicker
{
@@ -23,16 +12,19 @@ namespace ColorPicker
/// </summary>
public partial class ColorEditorWindow : Window
{
public ColorEditorWindow()
private readonly AppStateHandler _appStateHandler;
public ColorEditorWindow(AppStateHandler appStateHandler)
{
InitializeComponent();
_appStateHandler = appStateHandler;
Closing += ColorEditorWindow_Closing;
}
private void ColorEditorWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
_appStateHandler.EndUserSession();
}
}
}

View File

@@ -56,6 +56,7 @@ namespace ColorPicker.Controls
private void CopyToClipboardButton_Click(object sender, RoutedEventArgs e)
{
ClipboardHelper.CopyToClipboard(ColorTextRepresentationTextBlock.Text);
SessionEventHelper.Event.EditorColorCopiedToClipboard = true;
if (!_copyIndicatorVisible)
{
AppearCopiedIndicator();

View File

@@ -32,7 +32,8 @@
Background="LightPink"
Click="ColorVariationButton_Click"
AutomationProperties.Name="Color shade 1"
Style="{DynamicResource ColorShadeButtonStyle}"/>
Style="{DynamicResource ColorShadeButtonStyle}"
ToolTipService.ToolTip="{x:Static p:Resources.Select_color}"/>
<Button x:Name="colorVariation2Button"
Grid.Column="1"
ui:ControlHelper.CornerRadius="0"
@@ -40,7 +41,8 @@
Background="LightPink"
Click="ColorVariationButton_Click"
AutomationProperties.Name="Color shade 2"
Style="{DynamicResource ColorShadeButtonStyle}"/>
Style="{DynamicResource ColorShadeButtonStyle}"
ToolTipService.ToolTip="{x:Static p:Resources.Select_color}"/>
<Button x:Name="colorVariation3Button"
Grid.Column="3"
TabIndex="7"
@@ -48,7 +50,8 @@
Background="LightPink"
Click="ColorVariationButton_Click"
AutomationProperties.Name="Color shade 3"
Style="{DynamicResource ColorShadeButtonStyle}"/>
Style="{DynamicResource ColorShadeButtonStyle}"
ToolTipService.ToolTip="{x:Static p:Resources.Select_color}"/>
<Button x:Name="colorVariation4Button"
Grid.Column="4"
TabIndex="8"
@@ -56,7 +59,8 @@
Background="LightPink"
Click="ColorVariationButton_Click"
AutomationProperties.Name="Color shade 5"
Style="{DynamicResource ColorShadeButtonStyle}"/>
Style="{DynamicResource ColorShadeButtonStyle}"
ToolTipService.ToolTip="{x:Static p:Resources.Select_color}"/>
<Button x:Name="CurrentColorButton"
HorizontalAlignment="Left"
Grid.Column="0"
@@ -72,7 +76,8 @@
AutomationProperties.HelpText="{x:Static p:Resources.Selected_color_helptext}"
ToolTipService.ToolTip="{x:Static p:Resources.Selected_color_tooltip}"
Click="CurrentColorButton_Click"
Style="{DynamicResource ColorShadeButtonStyle}"/>
Style="{DynamicResource ColorShadeButtonStyle}">
</Button>
</Grid>
<!--Details panel-->
@@ -202,71 +207,73 @@
</Border>
</Grid>
<Grid HorizontalAlignment="Stretch" Margin="12,16,12,8">
<Grid HorizontalAlignment="Stretch"
Margin="12,16,12,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="68"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="86" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="36"/>
<RowDefinition Height="36"/>
<RowDefinition Height="36"/>
<RowDefinition Height="36" />
<RowDefinition Height="36" />
<RowDefinition Height="36" />
</Grid.RowDefinitions>
<TextBlock Text="R"
FontWeight="SemiBold"
VerticalAlignment="Center"/>
<TextBox x:Name="RTextBox"
Margin="0,0,0,0"
Grid.Column="1"
Height="32"
AutomationProperties.Name="{x:Static p:Resources.Red_value}"
TextChanged="RGBTextBoxes_TextChanged"
TextWrapping="Wrap"/>
VerticalAlignment="Center" />
<ui:NumberBox x:Name="RNumberBox"
Grid.Column="1"
Height="32"
AutomationProperties.Name="{x:Static p:Resources.Red_value}"
ValueChanged="RGBNumberBox_ValueChanged"
Minimum="0"
Maximum="255" />
<TextBlock Text="G"
FontWeight="SemiBold"
Grid.Row="1"
VerticalAlignment="Center"/>
VerticalAlignment="Center" />
<TextBox x:Name="GTextBox"
Width="68"
Height="32"
Grid.Row="1"
Grid.Column="1"
AutomationProperties.Name="{x:Static p:Resources.Green_value}"
TextChanged="RGBTextBoxes_TextChanged"
TextWrapping="Wrap"/>
<ui:NumberBox x:Name="GNumberBox"
Height="32"
Grid.Row="1"
Grid.Column="1"
AutomationProperties.Name="{x:Static p:Resources.Green_value}"
ValueChanged="RGBNumberBox_ValueChanged"
Minimum="0"
Maximum="255" />
<TextBlock Text="B"
FontWeight="SemiBold"
Grid.Row="2"
VerticalAlignment="Center"/>
VerticalAlignment="Center" />
<TextBox x:Name="BTextBox"
Width="68"
Height="32"
Grid.Column="1"
Grid.Row="2"
AutomationProperties.Name="{x:Static p:Resources.Blue_value}"
TextChanged="RGBTextBoxes_TextChanged"
TextWrapping="Wrap"/>
<ui:NumberBox x:Name="BNumberBox"
Height="32"
Grid.Column="1"
Grid.Row="2"
AutomationProperties.Name="{x:Static p:Resources.Blue_value}"
ValueChanged="RGBNumberBox_ValueChanged"
Minimum="0"
Maximum="255" />
<TextBlock Text="HEX"
Grid.Column="2"
HorizontalAlignment="Right"
FontWeight="SemiBold"
VerticalAlignment="Center"/>
VerticalAlignment="Center" />
<TextBox x:Name="HexCode"
HorizontalAlignment="Stretch"
Margin="8,0,0,0"
Height="32"
Grid.Column="3"
AutomationProperties.Name="{x:Static p:Resources.Hex_value}"
GotKeyboardFocus="HexCode_GotKeyboardFocus"
TextChanged="HexCode_TextChanged"
TextWrapping="Wrap"/>
TextWrapping="Wrap" />
</Grid>
<WrapPanel HorizontalAlignment="Right"
Margin="0,0,0,0"

View File

@@ -56,20 +56,24 @@ namespace ColorPicker.Controls
private static void SelectedColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (ColorPickerControl)d;
var newColor = (Color)e.NewValue;
((ColorPickerControl)d)._originalColor = ((ColorPickerControl)d)._currentColor = newColor;
var newColorBackground = new SolidColorBrush(newColor);
((ColorPickerControl)d).CurrentColorButton.Background = newColorBackground;
((ColorPickerControl)d)._ignoreHexChanges = true;
((ColorPickerControl)d)._ignoreRGBChanges = true;
((ColorPickerControl)d).HexCode.Text = ColorToHex(newColor);
((ColorPickerControl)d).RTextBox.Text = newColor.R.ToString(CultureInfo.InvariantCulture);
((ColorPickerControl)d).GTextBox.Text = newColor.G.ToString(CultureInfo.InvariantCulture);
((ColorPickerControl)d).BTextBox.Text = newColor.B.ToString(CultureInfo.InvariantCulture);
((ColorPickerControl)d).SetColorFromTextBoxes(System.Drawing.Color.FromArgb(newColor.R, newColor.G, newColor.B));
((ColorPickerControl)d)._ignoreRGBChanges = false;
((ColorPickerControl)d)._ignoreHexChanges = false;
control._originalColor = control._currentColor = newColor;
var newColorBackground = new SolidColorBrush(newColor);
control.CurrentColorButton.Background = newColorBackground;
control._ignoreHexChanges = true;
control._ignoreRGBChanges = true;
control.HexCode.Text = ColorToHex(newColor);
control.RNumberBox.Text = newColor.R.ToString(CultureInfo.InvariantCulture);
control.GNumberBox.Text = newColor.G.ToString(CultureInfo.InvariantCulture);
control.BNumberBox.Text = newColor.B.ToString(CultureInfo.InvariantCulture);
control.SetColorFromTextBoxes(System.Drawing.Color.FromArgb(newColor.R, newColor.G, newColor.B));
control._ignoreRGBChanges = false;
control._ignoreHexChanges = false;
var hsv = ColorHelper.ConvertToHSVColor(System.Drawing.Color.FromArgb(newColor.R, newColor.G, newColor.B));
@@ -107,12 +111,13 @@ namespace ColorPicker.Controls
}
var s = hsv.saturation;
var control = (ColorPickerControl)d;
((ColorPickerControl)d).colorVariation1Button.Background = new SolidColorBrush(HSVColor.RGBFromHSV(Math.Min(hsv.hue + (hueCoefficient * 8), 360), s, Math.Min(hsv.value + 0.3, 1)));
((ColorPickerControl)d).colorVariation2Button.Background = new SolidColorBrush(HSVColor.RGBFromHSV(Math.Min(hsv.hue + (hueCoefficient * 4), 360), s, Math.Min(hsv.value + 0.15, 1)));
control.colorVariation1Button.Background = new SolidColorBrush(HSVColor.RGBFromHSV(Math.Min(hsv.hue + (hueCoefficient * 8), 360), s, Math.Min(hsv.value + 0.3, 1)));
control.colorVariation2Button.Background = new SolidColorBrush(HSVColor.RGBFromHSV(Math.Min(hsv.hue + (hueCoefficient * 4), 360), s, Math.Min(hsv.value + 0.15, 1)));
((ColorPickerControl)d).colorVariation3Button.Background = new SolidColorBrush(HSVColor.RGBFromHSV(Math.Max(hsv.hue - (hueCoefficient2 * 4), 0), s, Math.Max(hsv.value - 0.2, 0)));
((ColorPickerControl)d).colorVariation4Button.Background = new SolidColorBrush(HSVColor.RGBFromHSV(Math.Max(hsv.hue - (hueCoefficient2 * 8), 0), s, Math.Max(hsv.value - 0.3, 0)));
control.colorVariation3Button.Background = new SolidColorBrush(HSVColor.RGBFromHSV(Math.Max(hsv.hue - (hueCoefficient2 * 4), 0), s, Math.Max(hsv.value - 0.2, 0)));
control.colorVariation4Button.Background = new SolidColorBrush(HSVColor.RGBFromHSV(Math.Max(hsv.hue - (hueCoefficient2 * 8), 0), s, Math.Max(hsv.value - 0.3, 0)));
}
private void UpdateValueColorGradient(double posX)
@@ -161,9 +166,9 @@ namespace ColorPicker.Controls
if (!_ignoreRGBChanges)
{
RTextBox.Text = currentColor.R.ToString(CultureInfo.InvariantCulture);
GTextBox.Text = currentColor.G.ToString(CultureInfo.InvariantCulture);
BTextBox.Text = currentColor.B.ToString(CultureInfo.InvariantCulture);
RNumberBox.Text = currentColor.R.ToString(CultureInfo.InvariantCulture);
GNumberBox.Text = currentColor.G.ToString(CultureInfo.InvariantCulture);
BNumberBox.Text = currentColor.B.ToString(CultureInfo.InvariantCulture);
}
_currentColor = currentColor;
@@ -200,6 +205,7 @@ namespace ColorPicker.Controls
detailsStackPanel.BeginAnimation(StackPanel.OpacityProperty, opacityAppear);
detailsGrid.BeginAnimation(Grid.HeightProperty, resize);
CurrentColorButton.IsEnabled = false;
SessionEventHelper.Event.EditorAdjustColorOpened = true;
}
}
@@ -234,8 +240,8 @@ namespace ColorPicker.Controls
private void OKButton_Click(object sender, RoutedEventArgs e)
{
HideDetails();
SelectedColorChangedCommand.Execute(_currentColor);
SessionEventHelper.Event.EditorColorAdjusted = true;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
@@ -253,6 +259,7 @@ namespace ColorPicker.Controls
{
var selectedColor = ((SolidColorBrush)((Button)sender).Background).Color;
SelectedColorChangedCommand.Execute(selectedColor);
SessionEventHelper.Event.EditorSimilarColorPicked = true;
}
private void ValueGradientGrid_MouseMove(object sender, MouseEventArgs e)
@@ -357,19 +364,15 @@ namespace ColorPicker.Controls
}
}
private void RGBTextBoxes_TextChanged(object sender, TextChangedEventArgs e)
#pragma warning disable CA1801 // Review unused parameters
private void RGBNumberBox_ValueChanged(ModernWpf.Controls.NumberBox sender, ModernWpf.Controls.NumberBoxValueChangedEventArgs args)
#pragma warning restore CA1801 // Review unused parameters
{
var validNumber = int.TryParse((sender as TextBox).Text, out int result);
if (!validNumber || result < 0 || result > 255)
{
return;
}
if (!_ignoreRGBChanges)
{
var r = byte.Parse(RTextBox.Text, CultureInfo.InvariantCulture);
var g = byte.Parse(GTextBox.Text, CultureInfo.InvariantCulture);
var b = byte.Parse(BTextBox.Text, CultureInfo.InvariantCulture);
var r = byte.Parse(RNumberBox.Text, CultureInfo.InvariantCulture);
var g = byte.Parse(GNumberBox.Text, CultureInfo.InvariantCulture);
var b = byte.Parse(BNumberBox.Text, CultureInfo.InvariantCulture);
_ignoreRGBChanges = true;
SetColorFromTextBoxes(System.Drawing.Color.FromArgb(r, g, b));
_ignoreRGBChanges = false;
@@ -397,5 +400,10 @@ namespace ColorPicker.Controls
{
return "#" + BitConverter.ToString(new byte[] { color.R, color.G, color.B }).Replace("-", string.Empty, StringComparison.InvariantCulture);
}
private void HexCode_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
(sender as TextBox).SelectAll();
}
}
}

View File

@@ -5,7 +5,9 @@
using System;
using System.ComponentModel.Composition;
using System.Windows;
using ColorPicker.Settings;
using ColorPicker.ViewModelContracts;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
namespace ColorPicker.Helpers
{
@@ -13,15 +15,17 @@ namespace ColorPicker.Helpers
public class AppStateHandler
{
private readonly IColorEditorViewModel _colorEditorViewModel;
private readonly IUserSettings _userSettings;
private ColorEditorWindow _colorEditorWindow;
private bool _colorPickerShown;
private object _colorPickerVisibilityLock = new object();
[ImportingConstructor]
public AppStateHandler(IColorEditorViewModel colorEditorViewModel)
public AppStateHandler(IColorEditorViewModel colorEditorViewModel, IUserSettings userSettings)
{
Application.Current.MainWindow.Closed += MainWindow_Closed;
_colorEditorViewModel = colorEditorViewModel;
_userSettings = userSettings;
}
public event EventHandler AppShown;
@@ -30,45 +34,61 @@ namespace ColorPicker.Helpers
public event EventHandler AppClosed;
public void ShowColorPicker()
public void StartUserSession()
{
lock (_colorPickerVisibilityLock)
{
if (!_colorPickerShown)
if (!_colorPickerShown && !IsColorPickerEditorVisible())
{
AppShown?.Invoke(this, EventArgs.Empty);
Application.Current.MainWindow.Opacity = 0;
Application.Current.MainWindow.Visibility = Visibility.Visible;
_colorPickerShown = true;
SessionEventHelper.Start(_userSettings.ActivationAction.Value);
}
if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor)
{
ShowColorPickerEditor();
}
else
{
ShowColorPicker();
}
}
}
public void HideColorPicker()
public void EndUserSession()
{
lock (_colorPickerVisibilityLock)
{
if (_colorPickerShown)
if (IsColorPickerEditorVisible() || _colorPickerShown)
{
Application.Current.MainWindow.Opacity = 0;
Application.Current.MainWindow.Visibility = Visibility.Collapsed;
AppHidden?.Invoke(this, EventArgs.Empty);
_colorPickerShown = false;
if (IsColorPickerEditorVisible())
{
HideColorPickerEditor();
}
else
{
HideColorPicker();
}
SessionEventHelper.End();
}
}
}
public void ShowColorPickerEditor()
public void OnColorPickerMouseDown()
{
if (_colorEditorWindow == null)
if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenColorPickerAndThenEditor || _userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor)
{
_colorEditorWindow = new ColorEditorWindow();
_colorEditorWindow.Content = _colorEditorViewModel;
_colorEditorViewModel.OpenColorPickerRequested += ColorEditorViewModel_OpenColorPickerRequested;
}
lock (_colorPickerVisibilityLock)
{
HideColorPicker();
}
_colorEditorViewModel.Initialize();
_colorEditorWindow.Show();
ShowColorPickerEditor();
}
else
{
EndUserSession();
}
}
public static void SetTopMost()
@@ -77,6 +97,65 @@ namespace ColorPicker.Helpers
Application.Current.MainWindow.Topmost = true;
}
private void ShowColorPicker()
{
if (!_colorPickerShown)
{
AppShown?.Invoke(this, EventArgs.Empty);
Application.Current.MainWindow.Opacity = 0;
Application.Current.MainWindow.Visibility = Visibility.Visible;
_colorPickerShown = true;
}
}
private void HideColorPicker()
{
if (_colorPickerShown)
{
Application.Current.MainWindow.Opacity = 0;
Application.Current.MainWindow.Visibility = Visibility.Collapsed;
AppHidden?.Invoke(this, EventArgs.Empty);
_colorPickerShown = false;
}
}
private void ShowColorPickerEditor()
{
if (_colorEditorWindow == null)
{
_colorEditorWindow = new ColorEditorWindow(this);
_colorEditorWindow.Content = _colorEditorViewModel;
_colorEditorViewModel.OpenColorPickerRequested += ColorEditorViewModel_OpenColorPickerRequested;
_colorEditorViewModel.OpenColorPickerRequested += (object sender, EventArgs e) =>
{
SessionEventHelper.Event.EditorColorPickerOpened = true;
};
}
_colorEditorViewModel.Initialize();
_colorEditorWindow.Show();
SessionEventHelper.Event.EditorOpened = true;
}
private void HideColorPickerEditor()
{
if (_colorEditorWindow != null)
{
_colorEditorWindow.Hide();
}
}
private bool IsColorPickerEditorVisible()
{
if (_colorEditorWindow != null)
{
// Check if we are visible and on top. Using focus producing unreliable results the first time the picker is opened.
return _colorEditorWindow.Topmost && _colorEditorWindow.IsVisible;
}
return false;
}
private void MainWindow_Closed(object sender, EventArgs e)
{
AppClosed?.Invoke(this, EventArgs.Empty);
@@ -84,7 +163,11 @@ namespace ColorPicker.Helpers
private void ColorEditorViewModel_OpenColorPickerRequested(object sender, EventArgs e)
{
ShowColorPicker();
lock (_colorPickerVisibilityLock)
{
ShowColorPicker();
}
_colorEditorWindow.Hide();
}
}

View File

@@ -5,14 +5,16 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Abstractions;
using interop;
namespace ColorPicker.Helpers
{
public static class Logger
{
private static readonly IFileSystem _fileSystem = new FileSystem();
private static readonly string ApplicationLogPath = _fileSystem.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ColorPicker");
private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "ColorPicker\\Logs");
static Logger()
{

View File

@@ -3,25 +3,13 @@
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel.Composition;
using System.Threading;
using System.Windows;
using interop;
namespace ColorPicker.Helpers
{
[Export(typeof(NativeEventWaiter))]
public class NativeEventWaiter
public static class NativeEventWaiter
{
private AppStateHandler _appStateHandler;
[ImportingConstructor]
public NativeEventWaiter(AppStateHandler appStateHandler)
{
_appStateHandler = appStateHandler;
WaitForEventLoop(Constants.ShowColorPickerSharedEvent(), _appStateHandler.ShowColorPicker);
}
public static void WaitForEventLoop(string eventName, Action callback)
{
new Thread(() =>
@@ -31,7 +19,7 @@ namespace ColorPicker.Helpers
{
if (eventHandle.WaitOne())
{
Logger.LogInfo("Successfully waited for SHOW_COLOR_PICKER_EVENT");
Logger.LogInfo($"Successfully waited for {eventName}");
Application.Current.Dispatcher.Invoke(callback);
}
}

View File

@@ -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;
using ColorPicker.Telemetry;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Telemetry;
namespace ColorPicker.Helpers
{
public static class SessionEventHelper
{
public static ColorPickerSession Event { get; private set; }
public static void Start(ColorPickerActivationAction startedAs)
{
Event = new ColorPickerSession();
Event.StartedAs = startedAs.ToString();
_startTime = DateTime.Now;
}
public static void End()
{
if (_startTime == null)
{
Logger.LogError("Failed to send ColorPickerSessionEvent");
return;
}
var duration = DateTime.Now - _startTime.Value;
Event.Duration = duration.Seconds + (duration.Milliseconds == 0 ? 0 : 1);
_startTime = null;
PowerToysTelemetry.Log.WriteEvent(Event);
}
private static DateTime? _startTime;
}
}

View File

@@ -179,7 +179,7 @@ namespace ColorPicker.Helpers
{
_zoomWindow.Left = _lastLeft + 1;
_zoomWindow.Top = _lastTop + 1;
PowerToysTelemetry.Log.WriteEvent(new ColorPickerZoomOpenedEvent());
SessionEventHelper.Event.ZoomUsed = true;
}
_throttledActionInvoker.ScheduleAction(

View File

@@ -5,7 +5,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Windows.Input;
using ColorPicker.Helpers;
using ColorPicker.Settings;
@@ -22,7 +21,7 @@ namespace ColorPicker.Keyboard
{
private readonly AppStateHandler _appStateHandler;
private readonly IUserSettings _userSettings;
private List<string> _previouslyPressedKeys;
private List<string> _previouslyPressedKeys = new List<string>();
private List<string> _activationKeys = new List<string>();
private GlobalKeyboardHook _keyboardHook;
@@ -73,47 +72,43 @@ namespace ColorPicker.Keyboard
// ESC pressed
if (virtualCode == KeyInterop.VirtualKeyFromKey(Key.Escape))
{
_appStateHandler.HideColorPicker();
PowerToysTelemetry.Log.WriteEvent(new ColorPickerCancelledEvent());
_appStateHandler.EndUserSession();
return;
}
var name = Helper.GetKeyName((uint)virtualCode);
// If the last key pressed is a modifier key, then currentlyPressedKeys cannot possibly match with _activationKeys
// because _activationKeys contains exactly 1 non-modifier key. Hence, there's no need to check if `name` is a
// modifier key or to do any additional processing on it.
if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown || e.KeyboardState == GlobalKeyboardHook.KeyboardState.SysKeyDown)
if ((System.Windows.Application.Current as ColorPickerUI.App).IsRunningDetachedFromPowerToys())
{
// Check pressed modifier keys.
AddModifierKeys(currentlyPressedKeys);
var name = Helper.GetKeyName((uint)virtualCode);
currentlyPressedKeys.Add(name);
}
currentlyPressedKeys.Sort();
if (currentlyPressedKeys.Count == 0 && _previouslyPressedKeys.Count != 0)
{
// no keys pressed, we can enable activation shortcut again
_activationShortcutPressed = false;
}
_previouslyPressedKeys = currentlyPressedKeys;
if (ArraysAreSame(currentlyPressedKeys, _activationKeys))
{
// avoid triggering this action multiple times as this will be called nonstop while keys are pressed
if (!_activationShortcutPressed)
// If the last key pressed is a modifier key, then currentlyPressedKeys cannot possibly match with _activationKeys
// because _activationKeys contains exactly 1 non-modifier key. Hence, there's no need to check if `name` is a
// modifier key or to do any additional processing on it.
if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown || e.KeyboardState == GlobalKeyboardHook.KeyboardState.SysKeyDown)
{
_activationShortcutPressed = true;
if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor)
// Check pressed modifier keys.
AddModifierKeys(currentlyPressedKeys);
currentlyPressedKeys.Add(name);
}
currentlyPressedKeys.Sort();
if (currentlyPressedKeys.Count == 0 && _previouslyPressedKeys.Count != 0)
{
// no keys pressed, we can enable activation shortcut again
_activationShortcutPressed = false;
}
_previouslyPressedKeys = currentlyPressedKeys;
if (ArraysAreSame(currentlyPressedKeys, _activationKeys))
{
// avoid triggering this action multiple times as this will be called nonstop while keys are pressed
if (!_activationShortcutPressed)
{
_appStateHandler.ShowColorPickerEditor();
}
else
{
_appStateHandler.ShowColorPicker();
_activationShortcutPressed = true;
_appStateHandler.StartUserSession();
}
}
}

View File

@@ -114,6 +114,7 @@ namespace ColorPicker.Mouse
{
MouseDevice mouseDev = InputManager.Current.PrimaryMouseDevice;
MouseWheel.Invoke(null, new MouseWheelEventArgs(mouseDev, Environment.TickCount, (int)mouseHookStruct.mouseData >> 16));
return new IntPtr(-1);
}
}
}

View File

@@ -195,6 +195,15 @@ namespace ColorPicker.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Select color.
/// </summary>
public static string Select_color {
get {
return ResourceManager.GetString("Select_color", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Selected color.
/// </summary>

View File

@@ -361,4 +361,7 @@
<value>Plum</value>
<comment>Plum color</comment>
</data>
<data name="Select_color" xml:space="preserve">
<value>Select color</value>
</data>
</root>

View File

@@ -105,6 +105,7 @@
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="False"
Opacity="0"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
@@ -113,12 +114,12 @@
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Background" Property="Opacity" Value="0.8" />
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" />
<Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
<Setter TargetName="ContentPresenter" Property="Opacity" Value="1" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Background" Property="Opacity" Value="0.9" />
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
<Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
<Setter TargetName="ContentPresenter" Property="Opacity" Value="1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

View File

@@ -25,5 +25,7 @@ namespace ColorPicker.Settings
ObservableCollection<string> VisibleColorFormats { get; }
SettingItem<bool> ShowColorName { get; }
void SendSettingsTelemetry();
}
}

View File

@@ -13,6 +13,7 @@ using ColorPicker.Common;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Telemetry;
namespace ColorPicker.Settings
{
@@ -160,5 +161,27 @@ namespace ColorPicker.Settings
}
}
}
public void SendSettingsTelemetry()
{
Logger.LogInfo("Sending settings telemetry");
var settings = _settingsUtils.GetSettingsOrDefault<ColorPickerSettings>(ColorPickerModuleName);
var properties = settings?.Properties;
if (properties == null)
{
Logger.LogError("Failed to send settings telemetry");
return;
}
var telemetrySettings = new Telemetry.ColorPickerSettings(properties.VisibleColorFormats)
{
ActivationShortcut = properties.ActivationShortcut.ToString(),
ActivationBehaviour = properties.ActivationAction.ToString(),
ColorFormatForClipboard = properties.CopiedColorRepresentation.ToString(),
ShowColorName = properties.ShowColorName,
};
PowerToysTelemetry.Log.WriteEvent(telemetrySettings);
}
}
}

View File

@@ -0,0 +1,44 @@
// 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.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace ColorPicker.Telemetry
{
[EventData]
public class ColorPickerSession : EventBase, IEvent
{
public ColorPickerSession()
{
EventName = "ColorPicker_Session";
}
public string StartedAs { get; set; }
public bool ZoomUsed { get; set; }
public bool EditorOpened { get; set; }
public bool EditorColorPickerOpened { get; set; }
public bool EditorAdjustColorOpened { get; set; }
public bool EditorColorAdjusted { get; set; }
public bool EditorSimilarColorPicked { get; set; }
public bool EditorHistoryColorPicked { get; set; }
public bool EditorHistoryColorRemoved { get; set; }
public bool EditorColorCopiedToClipboard { get; set; }
public int Duration { get; set; }
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View 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.Collections.Generic;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace ColorPicker.Telemetry
{
[EventData]
public class ColorPickerSettings : EventBase, IEvent
{
public ColorPickerSettings(IDictionary<string, bool> editorFormats)
{
EditorFormats = editorFormats;
EventName = "ColorPicker_Settings";
}
public string ActivationShortcut { get; set; }
public string ActivationBehaviour { get; set; }
public string ColorFormatForClipboard { get; set; }
public bool ShowColorName { get; set; }
public IDictionary<string, bool> EditorFormats { get; }
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@@ -1,16 +0,0 @@
// 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 ColorPicker.Telemetry
{
[EventData]
public class ColorPickerShowEvent : EventBase, IEvent
{
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@@ -1,16 +0,0 @@
// 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 ColorPicker.Telemetry
{
[EventData]
public class ColorPickerZoomOpenedEvent : EventBase, IEvent
{
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@@ -134,6 +134,7 @@ namespace ColorPicker.ViewModels
var indexToSelect = SelectedColorIndex == ColorsHistory.Count - 1 ? ColorsHistory.Count - 2 : SelectedColorIndex;
ColorsHistory.RemoveAt(SelectedColorIndex);
SelectedColorIndex = indexToSelect;
SessionEventHelper.Event.EditorHistoryColorRemoved = true;
}
private void SetupAllColorRepresentations()

View File

@@ -15,6 +15,7 @@ using ColorPicker.Mouse;
using ColorPicker.Settings;
using ColorPicker.Telemetry;
using ColorPicker.ViewModelContracts;
using interop;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Telemetry;
@@ -26,7 +27,6 @@ namespace ColorPicker.ViewModels
private readonly ZoomWindowHelper _zoomWindowHelper;
private readonly AppStateHandler _appStateHandler;
private readonly IUserSettings _userSettings;
private readonly NativeEventWaiter _nativeEventWaiter;
/// <summary>
/// Backing field for <see cref="OtherColor"/>
@@ -49,13 +49,13 @@ namespace ColorPicker.ViewModels
ZoomWindowHelper zoomWindowHelper,
AppStateHandler appStateHandler,
KeyboardMonitor keyboardMonitor,
NativeEventWaiter nativeEventWaiter,
IUserSettings userSettings)
{
_zoomWindowHelper = zoomWindowHelper;
_appStateHandler = appStateHandler;
_userSettings = userSettings;
_nativeEventWaiter = nativeEventWaiter;
NativeEventWaiter.WaitForEventLoop(Constants.ShowColorPickerSharedEvent(), _appStateHandler.StartUserSession);
NativeEventWaiter.WaitForEventLoop(Constants.ColorPickerSendSettingsTelemetryEvent(), _userSettings.SendSettingsTelemetry);
if (mouseInfoProvider != null)
{
@@ -140,14 +140,7 @@ namespace ColorPicker.ViewModels
_userSettings.ColorHistory.RemoveAt(_userSettings.ColorHistory.Count - 1);
}
_appStateHandler.HideColorPicker();
if (_userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenColorPickerAndThenEditor || _userSettings.ActivationAction.Value == ColorPickerActivationAction.OpenEditor)
{
_appStateHandler.ShowColorPickerEditor();
}
PowerToysTelemetry.Log.WriteEvent(new ColorPickerShowEvent());
_appStateHandler.OnColorPickerMouseDown();
}
private string GetColorString()

View File

@@ -20,13 +20,16 @@
<!-- Side bar -->
<Grid Background="{DynamicResource SecondaryBackgroundBrush}">
<ui:ListView Margin="0,48,0,0"
<ui:ListView x:Name="HistoryColors"
Margin="0,48,0,0"
Grid.Row="1"
Padding="0"
TabIndex="3"
ItemsSource="{Binding ColorsHistory}"
SelectedIndex="{Binding SelectedColorIndex}"
ItemContainerStyle="{DynamicResource ColorHistoryListViewStyle}">
ItemContainerStyle="{DynamicResource ColorHistoryListViewStyle}"
IsItemClickEnabled="True"
ItemClick="HistoryColors_ItemClick">
<ui:ListView.ContextMenu>
<ContextMenu Visibility="{Binding ColorsHistory.Count, Converter={StaticResource numberToVisibilityConverter}}">
<MenuItem Header="{x:Static p:Resources.Remove}"
@@ -75,7 +78,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0,1,0,0" />
<!-- Enable once we have settings linking available -->
<!--<Button Width="46"
Height="32"
@@ -87,7 +90,7 @@
Margin="0,0,46,0"
ToolTipService.ToolTip="{x:Static p:Resources.Open_settings}"
AutomationProperties.Name="{x:Static p:Resources.Open_settings}" />-->
<Button Width="64"
Height="32"
TabIndex="1"

View File

@@ -2,20 +2,8 @@
// 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.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ColorPicker.Helpers;
namespace ColorPicker.Views
{
@@ -26,5 +14,12 @@ namespace ColorPicker.Views
{
public ColorEditorView() =>
InitializeComponent();
private void HistoryColors_ItemClick(object sender, ModernWpf.Controls.ItemClickEventArgs e)
{
// Note: it does not handle clicking on the same color.
// More appropriate event would be SelectionChanged but we can not distinguish between user action and program action inside of it.
SessionEventHelper.Event.EditorHistoryColorPicked = true;
}
}
}

View File

@@ -28,13 +28,13 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="coverlet.collector" Version="1.3.0">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="coverlet.collector" Version="3.0.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.3" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.3" />
</ItemGroup>
<ItemGroup>

View File

@@ -14,6 +14,7 @@
#include <lib/FancyZonesWinHookEventIDs.h>
#include <lib/FancyZonesData.cpp>
#include <common/logger/logger.h>
#include <common/utils/logger_helper.h>
#include <common/utils/resources.h>
#include <common/utils/winapi_error.h>
#include <common/utils/window.h>
@@ -156,9 +157,19 @@ public:
{
app_name = GET_RESOURCE_STRING(IDS_FANCYZONES);
app_key = NonLocalizable::FancyZonesStr;
std::filesystem::path logFilePath(PTSettingsHelper::get_module_save_folder_location(app_key));
const auto appFolder = PTSettingsHelper::get_module_save_folder_location(app_key);
const std::filesystem::path logFolder = LoggerHelpers::get_log_folder_path(appFolder);
std::filesystem::path logFilePath(logFolder);
logFilePath.append(LogSettings::fancyZonesLogPath);
Logger::init(LogSettings::fancyZonesLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
std::filesystem::path oldLogFolder(appFolder);
oldLogFolder.append(LogSettings::fancyZonesOldLogPath);
LoggerHelpers::delete_old_log_folder(oldLogFolder);
LoggerHelpers::delete_other_versions_log_folders(appFolder, logFolder);
m_settings = MakeFancyZonesSettings(reinterpret_cast<HINSTANCE>(&__ImageBase), FancyZonesModule::get_name(), FancyZonesModule::get_key());
FancyZonesDataInstance().LoadFancyZonesData();
s_instance = this;

View File

@@ -135,15 +135,7 @@ namespace FancyZonesEditor
sb.AppendLine(ParsingErrorDataTag);
sb.AppendLine(parseResult.MalformedData);
string message = parseResult.Message + Environment.NewLine + Environment.NewLine + FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_User_Choice;
if (MessageBox.Show(message, FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_Title, MessageBoxButton.YesNo) == MessageBoxResult.No)
{
// TODO: log error
ShowExceptionReportMessageBox(sb.ToString());
Environment.Exit(0);
}
ShowExceptionReportMessageBox(sb.ToString());
MessageBox.Show(parseResult.Message, FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_Title, MessageBoxButton.OK);
}
MainWindowSettingsModel settings = ((App)Current).MainWindowSettings;

View File

@@ -16,7 +16,12 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border x:Name="ThumbBorder" Opacity="0" BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Border x:Name="ThumbBorder"
Opacity="0"
CornerRadius="0"
BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates" >
<VisualStateGroup.Transitions>
@@ -64,7 +69,9 @@
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource SystemControlBackgroundAccentBrush}"/>
<Setter Property="BorderBrush"
TargetName="border"
Value="{DynamicResource SystemControlBackgroundAccentBrush}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.6"/>
@@ -79,10 +86,9 @@
</Style>
</UserControl.Resources>
<Border BorderBrush="{DynamicResource LayoutPreviewZoneBorderBrush}"
<Border BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}"
Background="{DynamicResource CanvasZoneBackgroundBrush}"
CornerRadius="4"
Effect="{StaticResource ZoneDropShadow}"
CornerRadius="0"
BorderThickness="1">
<Grid x:Name="Frame">
<Grid.RowDefinitions>
@@ -129,15 +135,15 @@
<Thumb x:Name="Caption" Cursor="SizeAll" Background="Transparent" BorderThickness="3" Padding="4" Grid.Column="0" Grid.ColumnSpan="5" Margin="-1" Grid.Row="0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="Caption_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NResize" Cursor="SizeNS" BorderThickness="0,3,0,0" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SResize" Cursor="SizeNS" BorderThickness="0,0,0,3" Grid.Row="4" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="WResize" Cursor="SizeWE" BorderThickness="3,0,0,0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="EResize" Cursor="SizeWE" BorderThickness="0,0,3,0" Grid.Column="4" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NResize" Cursor="SizeNS" BorderThickness="0,2,0,0" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SResize" Cursor="SizeNS" BorderThickness="0,0,0,2" Grid.Row="4" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="WResize" Cursor="SizeWE" BorderThickness="2,0,0,0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="EResize" Cursor="SizeWE" BorderThickness="0,0,2,0" Grid.Column="4" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NWResize" Cursor="SizeNWSE" BorderThickness="3,3,0,0" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NEResize" Cursor="SizeNESW" BorderThickness="0,3,3,0" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SWResize" Cursor="SizeNESW" BorderThickness="3,0,0,3" Grid.Row="3" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SEResize" Cursor="SizeNWSE" BorderThickness="0,0,3,3" Grid.Row="3" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NWResize" Cursor="SizeNWSE" BorderThickness="2,2,0,0" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NEResize" Cursor="SizeNESW" BorderThickness="0,2,2,0" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SWResize" Cursor="SizeNESW" BorderThickness="2,0,0,2" Grid.Row="3" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SEResize" Cursor="SizeNWSE" BorderThickness="0,0,2,2" Grid.Row="3" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Button Content="&#xE894;"
BorderThickness="0"

View File

@@ -71,7 +71,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.IO.Abstractions" Version="12.2.5" />
<PackageReference Include="System.Text.Json" Version="4.7.2" />
<PackageReference Include="System.Text.Json" Version="5.0.1" />
</ItemGroup>
<ItemGroup>
<Resource Include="images\FancyZonesEditor.ico" />

File diff suppressed because it is too large Load Diff

View File

@@ -1,604 +0,0 @@
// 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.Windows.Controls;
using System.Windows.Controls.Primitives;
using FancyZonesEditor.Models;
namespace FancyZonesEditor
{
public class GridDragHandles
{
public GridDragHandles(UIElementCollection resizers, Action<object, DragDeltaEventArgs> dragDelta, Action<object, DragCompletedEventArgs> dragCompleted)
{
_resizers = resizers;
_dragDelta = dragDelta;
_dragCompleted = dragCompleted;
}
public void InitDragHandles(GridLayoutModel model)
{
if (_resizers.Count == 0)
{
int[,] indices = model.CellChildMap;
// horizontal resizers
for (int row = 0; row < model.Rows - 1; row++)
{
for (int col = 0; col < model.Columns; col++)
{
if (indices[row, col] != indices[row + 1, col])
{
int endCol = col + 1;
while (endCol < model.Columns && indices[row, endCol] != indices[row + 1, endCol])
{
endCol++;
}
AddDragHandle(Orientation.Horizontal, row, row + 1, col, endCol, row);
col = endCol - 1;
}
}
}
// vertical resizers
for (int col = 0; col < model.Columns - 1; col++)
{
for (int row = 0; row < model.Rows; row++)
{
if (indices[row, col] != indices[row, col + 1])
{
int endRow = row + 1;
while (endRow < model.Rows && indices[endRow, col] != indices[endRow, col + 1])
{
endRow++;
}
AddDragHandle(Orientation.Vertical, row, endRow, col, col + 1, col + model.Rows - 1);
row = endRow - 1;
}
}
}
}
}
public void AddDragHandle(Orientation orientation, int foundRow, int foundCol, GridLayoutModel model)
{
int[,] indices = model.CellChildMap;
int endRow = foundRow + 1;
while (endRow < model.Rows && indices[endRow, foundCol] == indices[endRow - 1, foundCol])
{
endRow++;
}
int endCol = foundCol + 1;
while (endCol < model.Columns && indices[foundRow, endCol] == indices[foundRow, endCol - 1])
{
endCol++;
}
int index = (orientation == Orientation.Horizontal) ? foundRow : foundCol + model.Rows - 1;
AddDragHandle(orientation, foundRow, endRow, foundCol, endCol, index);
}
public void AddDragHandle(Orientation orientation, int rowStart, int rowEnd, int colStart, int colEnd, int index)
{
GridResizer resizer = new GridResizer
{
Orientation = orientation,
StartRow = rowStart,
EndRow = rowEnd,
StartCol = colStart,
EndCol = colEnd,
};
resizer.DragDelta += (obj, eventArgs) => _dragDelta(obj, eventArgs);
resizer.DragCompleted += (obj, eventArgs) => _dragCompleted(obj, eventArgs);
if (index > _resizers.Count)
{
index = _resizers.Count;
}
_resizers.Insert(index, resizer);
}
public void UpdateForExistingVerticalSplit(GridLayoutModel model, int foundRow, int splitCol)
{
Func<GridResizer, bool> cmpr = (GridResizer resizer) =>
{
return resizer.Orientation == Orientation.Vertical && resizer.StartCol == splitCol;
};
Func<GridResizer, bool> endCmpr = (GridResizer resizer) =>
{
return resizer.EndRow == foundRow;
};
Func<GridResizer, bool> startCmpr = (GridResizer resizer) =>
{
return resizer.StartRow == foundRow + 1;
};
if (!UpdateDragHandlerForExistingSplit(Orientation.Vertical, cmpr, endCmpr, startCmpr))
{
AddDragHandle(Orientation.Vertical, foundRow, splitCol, model);
}
}
public void UpdateForExistingHorizontalSplit(GridLayoutModel model, int splitRow, int foundCol)
{
Func<GridResizer, bool> cmpr = (GridResizer resizer) =>
{
return resizer.Orientation == Orientation.Horizontal && resizer.StartRow == splitRow;
};
Func<GridResizer, bool> endCmpr = (GridResizer resizer) =>
{
return resizer.EndCol == foundCol;
};
Func<GridResizer, bool> startCmpr = (GridResizer resizer) =>
{
return resizer.StartCol == foundCol + 1;
};
if (!UpdateDragHandlerForExistingSplit(Orientation.Horizontal, cmpr, endCmpr, startCmpr))
{
AddDragHandle(Orientation.Horizontal, splitRow, foundCol, model);
}
}
/**
* Has to be called on split before adding new drag handle
*/
public void UpdateAfterVerticalSplit(int foundCol)
{
foreach (GridResizer r in _resizers)
{
if (r.StartCol > foundCol || (r.StartCol == foundCol && r.Orientation == Orientation.Vertical))
{
r.StartCol++;
}
if (r.EndCol > foundCol)
{
r.EndCol++;
}
}
}
/**
* Has to be called on split before adding new drag handle
*/
public void UpdateAfterHorizontalSplit(int foundRow)
{
foreach (GridResizer r in _resizers)
{
if (r.StartRow > foundRow || (r.StartRow == foundRow && r.Orientation == Orientation.Horizontal))
{
r.StartRow++;
}
if (r.EndRow > foundRow)
{
r.EndRow++;
}
}
}
public void UpdateAfterSwap(GridResizer resizer, double delta)
{
Orientation orientation = resizer.Orientation;
bool isHorizontal = orientation == Orientation.Horizontal;
bool isDeltaNegative = delta < 0;
List<GridResizer> swappedResizers = new List<GridResizer>();
if (isDeltaNegative)
{
DecreaseResizerValues(resizer, orientation);
}
else
{
IncreaseResizerValues(resizer, orientation);
}
// same orientation resizers update
foreach (GridResizer r in _resizers)
{
if (r.Orientation == orientation)
{
if ((isHorizontal && r.StartRow == resizer.StartRow && r.StartCol != resizer.StartCol) ||
(!isHorizontal && r.StartCol == resizer.StartCol && r.StartRow != resizer.StartRow))
{
if (isDeltaNegative)
{
IncreaseResizerValues(r, orientation);
}
else
{
DecreaseResizerValues(r, orientation);
}
swappedResizers.Add(r);
}
}
}
// different orientation resizers update
foreach (GridResizer r in _resizers)
{
if (r.Orientation != resizer.Orientation)
{
if (isHorizontal)
{
// vertical resizers corresponding to dragged resizer
if (r.StartCol >= resizer.StartCol && r.EndCol < resizer.EndCol)
{
if (r.StartRow == resizer.StartRow + 2 && isDeltaNegative)
{
r.StartRow--;
}
if (r.EndRow == resizer.EndRow + 1 && isDeltaNegative)
{
r.EndRow--;
}
if (r.StartRow == resizer.StartRow && !isDeltaNegative)
{
r.StartRow++;
}
if (r.EndRow == resizer.EndRow - 1 && !isDeltaNegative)
{
r.EndRow++;
}
}
else
{
// vertical resizers corresponding to swapped resizers
foreach (GridResizer sr in swappedResizers)
{
if (r.StartCol >= sr.StartCol && r.EndCol <= sr.EndCol)
{
if (r.StartRow == resizer.StartRow + 1 && isDeltaNegative)
{
r.StartRow++;
}
if (r.EndRow == resizer.EndRow && isDeltaNegative)
{
r.EndRow++;
}
if (r.StartRow == resizer.StartRow + 1 && !isDeltaNegative)
{
r.StartRow--;
}
if (r.EndRow == resizer.EndRow && !isDeltaNegative)
{
r.EndRow--;
}
}
}
}
}
else
{
// horizontal resizers corresponding to dragged resizer
if (r.StartRow >= resizer.StartRow && r.EndRow < resizer.EndRow)
{
if (r.StartCol == resizer.StartCol + 3 && isDeltaNegative)
{
r.StartCol--;
}
if (r.EndCol == resizer.EndCol + 1 && isDeltaNegative)
{
r.EndCol--;
}
if (r.StartCol == resizer.StartCol && !isDeltaNegative)
{
r.StartCol++;
}
if (r.EndCol == resizer.EndCol - 1 && !isDeltaNegative)
{
r.EndCol++;
}
}
else
{
// horizontal resizers corresponding to swapped resizers
foreach (GridResizer sr in swappedResizers)
{
if (r.StartRow >= sr.StartRow && r.EndRow <= sr.EndRow)
{
if (r.StartCol == resizer.StartCol + 1 && isDeltaNegative)
{
r.StartCol++;
}
if (r.EndCol == resizer.EndCol && isDeltaNegative)
{
r.EndCol++;
}
if (r.StartCol == resizer.StartCol + 1 && !isDeltaNegative)
{
r.StartCol--;
}
if (r.EndCol == resizer.EndCol && !isDeltaNegative)
{
r.EndCol--;
}
}
}
}
}
}
}
}
public void UpdateAfterDetach(GridResizer resizer, double delta)
{
bool isDeltaNegative = delta < 0;
Orientation orientation = resizer.Orientation;
foreach (GridResizer r in _resizers)
{
bool notEqual = r.StartRow != resizer.StartRow || r.EndRow != resizer.EndRow || r.StartCol != resizer.StartCol || r.EndCol != resizer.EndCol;
if (r.Orientation == orientation && notEqual)
{
if (orientation == Orientation.Horizontal)
{
if (r.StartRow > resizer.StartRow || (r.StartRow == resizer.StartRow && isDeltaNegative))
{
r.StartRow++;
}
if (r.EndRow > resizer.EndRow || (r.EndRow == resizer.EndRow && isDeltaNegative))
{
r.EndRow++;
}
}
else
{
if (r.StartCol > resizer.StartCol || (r.StartCol == resizer.StartCol && isDeltaNegative))
{
r.StartCol++;
}
if (r.EndCol > resizer.EndCol || (r.EndCol == resizer.EndCol && isDeltaNegative))
{
r.EndCol++;
}
}
}
}
if (!isDeltaNegative)
{
IncreaseResizerValues(resizer, orientation);
}
foreach (GridResizer r in _resizers)
{
if (r.Orientation != orientation)
{
if (orientation == Orientation.Vertical)
{
if (isDeltaNegative)
{
bool isRowNonAdjacent = r.EndRow < resizer.StartRow || r.StartRow > resizer.EndRow;
if (r.StartCol > resizer.StartCol + 1 || (r.StartCol == resizer.StartCol + 1 && isRowNonAdjacent))
{
r.StartCol++;
}
if (r.EndCol > resizer.EndCol || (r.EndCol == resizer.EndCol && isRowNonAdjacent))
{
r.EndCol++;
}
}
else
{
if (r.StartCol > resizer.StartCol || (r.StartCol == resizer.StartCol && r.StartRow >= resizer.StartRow && r.EndRow <= resizer.EndRow))
{
r.StartCol++;
}
if (r.EndCol > resizer.EndCol - 1 || (r.EndCol == resizer.EndCol - 1 && r.StartRow >= resizer.StartRow && r.EndRow <= resizer.EndRow))
{
r.EndCol++;
}
}
}
else
{
if (isDeltaNegative)
{
bool isColNonAdjacent = r.EndCol < resizer.StartCol || r.StartCol > resizer.EndCol;
if (r.StartRow > resizer.StartRow + 1 || (r.StartRow == resizer.StartRow + 1 && isColNonAdjacent))
{
r.StartRow++;
}
if (r.EndRow > resizer.EndRow || (r.EndRow == resizer.EndRow && isColNonAdjacent))
{
r.EndRow++;
}
}
else
{
if (r.StartRow > resizer.StartRow || (r.StartRow == resizer.StartRow && r.StartCol >= resizer.StartCol && r.EndCol <= resizer.EndCol))
{
r.StartRow++;
}
if (r.EndRow > resizer.EndRow - 1 || (r.EndRow == resizer.EndRow - 1 && r.StartCol >= resizer.StartCol && r.EndCol <= resizer.EndCol))
{
r.EndRow++;
}
}
}
}
}
}
public void RemoveDragHandles()
{
_resizers.Clear();
}
public bool HasSnappedNonAdjacentResizers(GridResizer resizer)
{
/**
* Resizers between zones 0,1 and 4,5 are snapped to each other and not adjacent.
* ------------------------------
* | 0 | 1 |
* ------------------------------
* | 2 | 3 |
* ------------------------------
* | 4 | 5 |
* ------------------------------
*
* Resizers between zones 0,1 and 2,3 are snapped to each other and adjacent.
* ------------------------------
* | 0 | 1 |
* ------------------------------
* | 2 | 3 |
* ------------------------------
* | 4 | 5 |
* ------------------------------
*
* Vertical resizers should have same StartColumn and different StartRow.
* Horizontal resizers should have same StartRow and different StartColumn.
* Difference between rows or columns should be more than 1.
*/
foreach (GridResizer r in _resizers)
{
if (r.Orientation == resizer.Orientation)
{
bool isHorizontalSnapped = resizer.Orientation == Orientation.Horizontal && r.StartRow == resizer.StartRow && (Math.Abs(resizer.StartCol - r.StartCol) > 1);
bool isVerticalSnapped = resizer.Orientation == Orientation.Vertical && r.StartCol == resizer.StartCol && (Math.Abs(resizer.StartRow - r.StartRow) > 1);
if (isHorizontalSnapped || isVerticalSnapped)
{
return true;
}
}
}
return false;
}
private static void IncreaseResizerValues(GridResizer resizer, Orientation orientation)
{
if (orientation == Orientation.Vertical)
{
resizer.StartCol++;
resizer.EndCol++;
}
else
{
resizer.StartRow++;
resizer.EndRow++;
}
}
private static void DecreaseResizerValues(GridResizer resizer, Orientation orientation)
{
if (orientation == Orientation.Vertical)
{
resizer.StartCol--;
resizer.EndCol--;
}
else
{
resizer.StartRow--;
resizer.EndRow--;
}
}
private bool UpdateDragHandlerForExistingSplit(Orientation orientation, Func<GridResizer, bool> cmpr, Func<GridResizer, bool> endCmpr, Func<GridResizer, bool> startCmpr)
{
bool updCurrentResizers = false;
GridResizer leftNeighbour = null;
GridResizer rightNeighbour = null;
for (int i = 0; i < _resizers.Count && (leftNeighbour == null || rightNeighbour == null); i++)
{
GridResizer resizer = (GridResizer)_resizers[i];
if (cmpr(resizer))
{
if (leftNeighbour == null && endCmpr(resizer))
{
leftNeighbour = resizer;
updCurrentResizers = true;
}
if (rightNeighbour == null && startCmpr(resizer))
{
rightNeighbour = resizer;
updCurrentResizers = true;
}
}
}
if (updCurrentResizers)
{
if (leftNeighbour != null && rightNeighbour != null)
{
if (orientation == Orientation.Vertical)
{
leftNeighbour.EndRow = rightNeighbour.EndRow;
}
else
{
leftNeighbour.EndCol = rightNeighbour.EndCol;
}
_resizers.Remove(rightNeighbour);
}
else if (leftNeighbour != null)
{
if (orientation == Orientation.Vertical)
{
leftNeighbour.EndRow++;
}
else
{
leftNeighbour.EndCol++;
}
}
else if (rightNeighbour != null)
{
if (orientation == Orientation.Vertical)
{
rightNeighbour.StartRow--;
}
else
{
rightNeighbour.StartCol--;
}
}
}
return updCurrentResizers;
}
private readonly UIElementCollection _resizers;
private readonly Action<object, DragDeltaEventArgs> _dragDelta;
private readonly Action<object, DragCompletedEventArgs> _dragCompleted;
}
}

View File

@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -20,6 +21,9 @@ namespace FancyZonesEditor
private const string PropertyRowsChangedID = "Rows";
private const string PropertyColumnsChangedID = "Columns";
private const string ObjectDependencyID = "Model";
private const string PropertyIsShiftKeyPressedID = "IsShiftKeyPressed";
private const int MinZoneSize = 100;
public static readonly DependencyProperty ModelProperty = DependencyProperty.Register(ObjectDependencyID, typeof(GridLayoutModel), typeof(GridEditor), new PropertyMetadata(null, OnGridDimensionsChanged));
@@ -27,13 +31,15 @@ namespace FancyZonesEditor
private int gridEditorUniqueId;
private GridData _data;
public GridEditor()
{
InitializeComponent();
Loaded += GridEditor_Loaded;
Unloaded += GridEditor_Unloaded;
((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
gridEditorUniqueId = ++gridEditorUniqueIdCounter;
((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
}
private void GridEditor_Loaded(object sender, RoutedEventArgs e)
@@ -45,21 +51,127 @@ namespace FancyZonesEditor
}
_data = new GridData(model);
_dragHandles = new GridDragHandles(AdornerLayer.Children, Resizer_DragDelta, Resizer_DragCompleted);
_dragHandles.InitDragHandles(model);
Model = model;
Model.PropertyChanged += OnGridDimensionsChanged;
SetupUI();
}
int zoneCount = _data.ZoneCount;
for (int i = 0; i < zoneCount; i++)
private void PlaceResizer(GridResizer resizerThumb)
{
var leftZone = Preview.Children[resizerThumb.LeftReferenceZone];
var rightZone = Preview.Children[resizerThumb.RightReferenceZone];
var topZone = Preview.Children[resizerThumb.TopReferenceZone];
var bottomZone = Preview.Children[resizerThumb.BottomReferenceZone];
double left = Canvas.GetLeft(leftZone);
double right = Canvas.GetLeft(rightZone) + (rightZone as GridZone).MinWidth;
double top = Canvas.GetTop(topZone);
double bottom = Canvas.GetTop(bottomZone) + (bottomZone as GridZone).MinHeight;
double x = (left + right) / 2.0;
double y = (top + bottom) / 2.0;
Canvas.SetLeft(resizerThumb, x - 24);
Canvas.SetTop(resizerThumb, y - 24);
}
private void SetZonePanelSize(GridZone panel, GridData.Zone zone)
{
Size actualSize = WorkAreaSize();
double spacing = Model.ShowSpacing ? Model.Spacing : 0;
double topSpacing = zone.Top == 0 ? spacing : spacing / 2;
double bottomSpacing = zone.Bottom == GridData.Multiplier ? spacing : spacing / 2;
double leftSpacing = zone.Left == 0 ? spacing : spacing / 2;
double rightSpacing = zone.Right == GridData.Multiplier ? spacing : spacing / 2;
Canvas.SetTop(panel, (actualSize.Height * zone.Top / GridData.Multiplier) + topSpacing);
Canvas.SetLeft(panel, (actualSize.Width * zone.Left / GridData.Multiplier) + leftSpacing);
panel.MinWidth = Math.Max(1, (actualSize.Width * (zone.Right - zone.Left) / GridData.Multiplier) - leftSpacing - rightSpacing);
panel.MinHeight = Math.Max(1, (actualSize.Height * (zone.Bottom - zone.Top) / GridData.Multiplier) - topSpacing - bottomSpacing);
}
private void SetupUI()
{
Size actualSize = WorkAreaSize();
if (actualSize.Width < 1 || _data == null || Model == null)
{
AddZone();
return;
}
Rect workingArea = App.Overlay.WorkArea;
Size actualSize = new Size(workingArea.Width, workingArea.Height);
ArrangeGridRects(actualSize);
int spacing = Model.ShowSpacing ? Model.Spacing : 0;
_data.MinZoneWidth = Convert.ToInt32(GridData.Multiplier / actualSize.Width * (MinZoneSize + (2 * spacing)));
_data.MinZoneHeight = Convert.ToInt32(GridData.Multiplier / actualSize.Height * (MinZoneSize + (2 * spacing)));
Preview.Children.Clear();
AdornerLayer.Children.Clear();
Preview.Width = actualSize.Width;
Preview.Height = actualSize.Height;
MagneticSnap snapX = new MagneticSnap(GridData.PrefixSum(Model.ColumnPercents).GetRange(1, Model.ColumnPercents.Count - 1), actualSize.Width);
MagneticSnap snapY = new MagneticSnap(GridData.PrefixSum(Model.RowPercents).GetRange(1, Model.RowPercents.Count - 1), actualSize.Height);
for (int zoneIndex = 0; zoneIndex < _data.Zones.Count(); zoneIndex++)
{
// this is needed for the lambda
int zoneIndexCopy = zoneIndex;
var zone = _data.Zones[zoneIndex];
var zonePanel = new GridZone(spacing, snapX, snapY, (orientation, offset) => _data.CanSplit(zoneIndexCopy, offset, orientation), zone);
zonePanel.UpdateShiftState(((App)Application.Current).MainWindowSettings.IsShiftKeyPressed);
Preview.Children.Add(zonePanel);
zonePanel.Split += OnSplit;
zonePanel.MergeDrag += OnMergeDrag;
zonePanel.MergeComplete += OnMergeComplete;
SetZonePanelSize(zonePanel, zone);
zonePanel.LabelID.Content = zoneIndex + 1;
}
foreach (var resizer in _data.Resizers)
{
var resizerThumb = new GridResizer();
resizerThumb.DragStarted += Resizer_DragStarted;
resizerThumb.DragDelta += Resizer_DragDelta;
resizerThumb.DragCompleted += Resizer_DragCompleted;
resizerThumb.Orientation = resizer.Orientation;
AdornerLayer.Children.Add(resizerThumb);
if (resizer.Orientation == Orientation.Horizontal)
{
resizerThumb.LeftReferenceZone = resizer.PositiveSideIndices[0];
resizerThumb.RightReferenceZone = resizer.PositiveSideIndices.Last();
resizerThumb.TopReferenceZone = resizer.PositiveSideIndices[0];
resizerThumb.BottomReferenceZone = resizer.NegativeSideIndices[0];
}
else
{
resizerThumb.LeftReferenceZone = resizer.PositiveSideIndices[0];
resizerThumb.RightReferenceZone = resizer.NegativeSideIndices[0];
resizerThumb.TopReferenceZone = resizer.PositiveSideIndices[0];
resizerThumb.BottomReferenceZone = resizer.PositiveSideIndices.Last();
}
PlaceResizer(resizerThumb);
}
}
private void OnSplit(object sender, SplitEventArgs args)
{
MergeCancelClick(null, null);
var zonePanel = sender as GridZone;
int zoneIndex = Preview.Children.IndexOf(zonePanel);
if (_data.CanSplit(zoneIndex, args.Offset, args.Orientation))
{
_data.Split(zoneIndex, args.Offset, args.Orientation);
SetupUI();
}
}
private void GridEditor_Unloaded(object sender, RoutedEventArgs e)
@@ -67,16 +179,10 @@ namespace FancyZonesEditor
gridEditorUniqueId = -1;
}
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
private Size WorkAreaSize()
{
Rect workingArea = App.Overlay.WorkArea;
Size actualSize = new Size(workingArea.Width, workingArea.Height);
// Only enter if this is the newest instance
if (actualSize.Width > 0 && gridEditorUniqueId == gridEditorUniqueIdCounter)
{
ArrangeGridRects(actualSize);
}
return new Size(workingArea.Width, workingArea.Height);
}
public GridLayoutModel Model
@@ -90,258 +196,6 @@ namespace FancyZonesEditor
get { return Preview; }
}
private void OnFullSplit(object o, SplitEventArgs e)
{
UIElementCollection previewChildren = Preview.Children;
UIElement splitee = (UIElement)o;
GridLayoutModel model = Model;
int spliteeIndex = previewChildren.IndexOf(splitee);
int rows = model.Rows;
int cols = model.Columns;
_startRow = -1;
_startCol = -1;
for (int row = rows - 1; row >= 0; row--)
{
for (int col = cols - 1; col >= 0; col--)
{
if (model.CellChildMap[row, col] == spliteeIndex)
{
_dragHandles.RemoveDragHandles();
_startRow = _endRow = row;
_startCol = _endCol = col;
ExtendRangeToHaveEvenCellEdges();
for (row = _startRow; row <= _endRow; row++)
{
for (col = _startCol; col <= _endCol; col++)
{
if ((row != _startRow) || (col != _startCol))
{
model.CellChildMap[row, col] = AddZone();
}
}
}
OnGridDimensionsChanged();
return;
}
}
}
}
private void ExtendRangeToHaveEvenCellEdges()
{
// As long as there is an edge of the 2D range such that some zone crosses its boundary, extend
// that boundary. A single pass is not enough, a while loop is needed. This results in the unique
// smallest rectangle containing the initial range such that no zone is "broken", meaning that
// some part of it is inside the 2D range, and some part is outside.
GridLayoutModel model = Model;
bool possiblyBroken = true;
while (possiblyBroken)
{
possiblyBroken = false;
for (int col = _startCol; col <= _endCol; col++)
{
if (_startRow > 0 && model.CellChildMap[_startRow - 1, col] == model.CellChildMap[_startRow, col])
{
_startRow--;
possiblyBroken = true;
break;
}
if (_endRow < model.Rows - 1 && model.CellChildMap[_endRow + 1, col] == model.CellChildMap[_endRow, col])
{
_endRow++;
possiblyBroken = true;
break;
}
}
for (int row = _startRow; row <= _endRow; row++)
{
if (_startCol > 0 && model.CellChildMap[row, _startCol - 1] == model.CellChildMap[row, _startCol])
{
_startCol--;
possiblyBroken = true;
break;
}
if (_endCol < model.Columns - 1 && model.CellChildMap[row, _endCol + 1] == model.CellChildMap[row, _endCol])
{
_endCol++;
possiblyBroken = true;
break;
}
}
}
}
private void OnSplit(object o, SplitEventArgs e)
{
MergeCancelClick(null, null);
UIElementCollection previewChildren = Preview.Children;
GridZone splitee = (GridZone)o;
int spliteeIndex = previewChildren.IndexOf(splitee);
GridLayoutModel model = Model;
int rows = model.Rows;
int cols = model.Columns;
Tuple<int, int> rowCol = _data.RowColByIndex(spliteeIndex);
int foundRow = rowCol.Item1;
int foundCol = rowCol.Item2;
int newChildIndex = AddZone();
double offset = e.Offset;
double space = e.Space;
if (e.Orientation == Orientation.Vertical)
{
if (splitee.VerticalSnapPoints != null)
{
offset += Canvas.GetLeft(splitee);
int count = splitee.VerticalSnapPoints.Length;
bool foundExistingSplit = false;
int splitCol = foundCol;
for (int i = 0; i <= count; i++)
{
if (foundExistingSplit)
{
int walkRow = foundRow;
while ((walkRow < rows) && (_data.GetIndex(walkRow, foundCol + i) == spliteeIndex))
{
_data.SetIndex(walkRow++, foundCol + i, newChildIndex);
}
}
if (_data.ColumnBottom(foundCol + i) == offset)
{
foundExistingSplit = true;
splitCol = foundCol + i;
// use existing division
}
}
if (foundExistingSplit)
{
_data.ReplaceIndicesToMaintainOrder(Preview.Children.Count);
_dragHandles.UpdateForExistingVerticalSplit(model, foundRow, splitCol);
OnGridDimensionsChanged();
return;
}
while (_data.ColumnBottom(foundCol) < offset)
{
foundCol++;
}
offset -= _data.ColumnTop(foundCol);
}
_dragHandles.UpdateAfterVerticalSplit(foundCol);
_data.SplitColumn(foundCol, spliteeIndex, newChildIndex, space, offset, App.Overlay.WorkArea.Width);
_dragHandles.AddDragHandle(Orientation.Vertical, foundRow, foundCol, model);
}
else
{
// Horizontal
if (splitee.HorizontalSnapPoints != null)
{
offset += Canvas.GetTop(splitee);
int count = splitee.HorizontalSnapPoints.Length;
bool foundExistingSplit = false;
int splitRow = foundRow;
for (int i = 0; i <= count; i++)
{
if (foundExistingSplit)
{
int walkCol = foundCol;
while ((walkCol < cols) && (_data.GetIndex(foundRow + i, walkCol) == spliteeIndex))
{
_data.SetIndex(foundRow + i, walkCol++, newChildIndex);
}
}
if (_data.RowEnd(foundRow + i) == offset)
{
foundExistingSplit = true;
splitRow = foundRow + i;
// use existing division
}
}
if (foundExistingSplit)
{
_data.ReplaceIndicesToMaintainOrder(Preview.Children.Count);
_dragHandles.UpdateForExistingHorizontalSplit(model, splitRow, foundCol);
OnGridDimensionsChanged();
return;
}
while (_data.RowEnd(foundRow) < offset)
{
foundRow++;
}
offset -= _data.RowStart(foundRow);
}
_dragHandles.UpdateAfterHorizontalSplit(foundRow);
_data.SplitRow(foundRow, spliteeIndex, newChildIndex, space, offset, App.Overlay.WorkArea.Height);
_dragHandles.AddDragHandle(Orientation.Horizontal, foundRow, foundCol, model);
}
var workArea = App.Overlay.WorkArea;
Size actualSize = new Size(workArea.Width, workArea.Height);
ArrangeGridRects(actualSize);
}
private void DeleteZone(int index)
{
Preview.Children.RemoveAt(index);
}
private int AddZone()
{
GridZone zone;
if (Model != null)
{
IList<int> freeZones = Model.FreeZones;
// first check free list
if (freeZones.Count > 0)
{
int freeIndex = freeZones[0];
freeZones.RemoveAt(0);
zone = (GridZone)Preview.Children[freeIndex];
zone.Visibility = Visibility.Visible;
return freeIndex;
}
zone = new GridZone(Model.ShowSpacing ? Model.Spacing : 0);
zone.Split += OnSplit;
zone.MergeDrag += OnMergeDrag;
zone.MergeComplete += OnMergeComplete;
zone.FullSplit += OnFullSplit;
Preview.Children.Add(zone);
return Preview.Children.Count - 1;
}
return 0;
}
private void OnGridDimensionsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
// Only enter if this is the newest instance
@@ -351,230 +205,201 @@ namespace FancyZonesEditor
}
}
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if ((e.PropertyName == PropertyIsShiftKeyPressedID) && gridEditorUniqueId == gridEditorUniqueIdCounter)
{
foreach (var child in Preview.Children)
{
var zone = child as GridZone;
zone.UpdateShiftState(((App)Application.Current).MainWindowSettings.IsShiftKeyPressed);
}
}
}
private static void OnGridDimensionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((GridEditor)d).OnGridDimensionsChanged();
((GridEditor)d).SetupUI();
}
private void OnGridDimensionsChanged()
{
Rect workingArea = App.Overlay.WorkArea;
Size actualSize = new Size(workingArea.Width, workingArea.Height);
if (actualSize.Width > 0)
{
ArrangeGridRects(actualSize);
}
SetupUI();
}
private void ArrangeGridRects(Size arrangeSize)
private double _dragX = 0;
private double _dragY = 0;
private void Resizer_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
{
var workArea = App.Overlay.WorkArea;
Preview.Width = workArea.Width;
Preview.Height = workArea.Height;
GridLayoutModel model = Model;
if (model == null || _data == null)
{
return;
}
if (model.Rows != model.RowPercents.Count || model.Columns != model.ColumnPercents.Count)
{
// Merge was not finished
return;
}
int spacing = model.ShowSpacing ? model.Spacing : 0;
_data.RecalculateZones(spacing, arrangeSize);
_data.ArrangeZones(Preview.Children, spacing);
_dragHandles.InitDragHandles(model);
_data.ArrangeResizers(AdornerLayer.Children, spacing);
_dragX = 0;
_dragY = 0;
}
private void Resizer_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
MergeCancelClick(null, null);
GridResizer resizer = (GridResizer)sender;
_dragX += e.HorizontalChange;
_dragY += e.VerticalChange;
double delta = (resizer.Orientation == Orientation.Vertical) ? e.HorizontalChange : e.VerticalChange;
if (delta == 0)
GridResizer resizer = (GridResizer)sender;
int resizerIndex = AdornerLayer.Children.IndexOf(resizer);
Size actualSize = WorkAreaSize();
int delta;
if (resizer.Orientation == Orientation.Vertical)
{
return;
delta = Convert.ToInt32(_dragX / actualSize.Width * GridData.Multiplier);
}
else
{
delta = Convert.ToInt32(_dragY / actualSize.Height * GridData.Multiplier);
}
GridData.ResizeInfo resizeInfo = _data.CalculateResizeInfo(resizer, delta);
if (resizeInfo.IsResizeAllowed)
if (_data.CanDrag(resizerIndex, delta))
{
if (_dragHandles.HasSnappedNonAdjacentResizers(resizer))
// Just update the UI, don't tell _data
if (resizer.Orientation == Orientation.Vertical)
{
double spacing = 0;
GridLayoutModel model = Model;
if (model.ShowSpacing)
_data.Resizers[resizerIndex].PositiveSideIndices.ForEach((zoneIndex) =>
{
spacing = model.Spacing;
}
var zone = Preview.Children[zoneIndex];
Canvas.SetLeft(zone, Canvas.GetLeft(zone) + e.HorizontalChange);
(zone as GridZone).MinWidth -= e.HorizontalChange;
});
_data.SplitOnDrag(resizer, delta, spacing);
_dragHandles.UpdateAfterDetach(resizer, delta);
_data.Resizers[resizerIndex].NegativeSideIndices.ForEach((zoneIndex) =>
{
var zone = Preview.Children[zoneIndex];
Canvas.SetRight(zone, Canvas.GetRight(zone) + e.HorizontalChange);
(zone as GridZone).MinWidth += e.HorizontalChange;
});
Canvas.SetLeft(resizer, Canvas.GetLeft(resizer) + e.HorizontalChange);
}
else
{
_data.DragResizer(resizer, resizeInfo);
if (_data.SwapNegativePercents(resizer.Orientation, resizer.StartRow, resizer.EndRow, resizer.StartCol, resizer.EndCol))
_data.Resizers[resizerIndex].PositiveSideIndices.ForEach((zoneIndex) =>
{
_dragHandles.UpdateAfterSwap(resizer, delta);
var zone = Preview.Children[zoneIndex];
Canvas.SetTop(zone, Canvas.GetTop(zone) + e.VerticalChange);
(zone as GridZone).MinHeight -= e.VerticalChange;
});
_data.Resizers[resizerIndex].NegativeSideIndices.ForEach((zoneIndex) =>
{
var zone = Preview.Children[zoneIndex];
Canvas.SetBottom(zone, Canvas.GetBottom(zone) + e.VerticalChange);
(zone as GridZone).MinHeight += e.VerticalChange;
});
Canvas.SetTop(resizer, Canvas.GetTop(resizer) + e.VerticalChange);
}
foreach (var child in AdornerLayer.Children)
{
GridResizer resizerThumb = child as GridResizer;
if (resizerThumb != resizer)
{
PlaceResizer(resizerThumb);
}
}
}
Rect workingArea = App.Overlay.WorkArea;
Size actualSize = new Size(workingArea.Width, workingArea.Height);
ArrangeGridRects(actualSize);
AdornerLayer.UpdateLayout();
else
{
// Undo changes
_dragX -= e.HorizontalChange;
_dragY -= e.VerticalChange;
}
}
private void Resizer_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
{
GridResizer resizer = (GridResizer)sender;
int index = _data.SwappedIndexAfterResize(resizer);
if (index != -1)
{
Rect workingArea = App.Overlay.WorkArea;
Size actualSize = new Size(workingArea.Width, workingArea.Height);
ArrangeGridRects(actualSize);
}
}
int resizerIndex = AdornerLayer.Children.IndexOf(resizer);
Size actualSize = WorkAreaSize();
private Point _startDragPos = new Point(-1, -1);
double pixelDelta = resizer.Orientation == Orientation.Vertical ?
_dragX / actualSize.Width * GridData.Multiplier :
_dragY / actualSize.Height * GridData.Multiplier;
_data.Drag(resizerIndex, Convert.ToInt32(pixelDelta));
SetupUI();
}
private void OnMergeComplete(object o, MouseButtonEventArgs e)
{
Point mousePoint = e.GetPosition(Preview);
_startDragPos = new Point(-1, -1);
_inMergeDrag = false;
int mergedIndex = Model.CellChildMap[_startRow, _startCol];
for (int row = _startRow; row <= _endRow; row++)
var selectedIndices = new List<int>();
for (int zoneIndex = 0; zoneIndex < _data.Zones.Count; zoneIndex++)
{
for (int col = _startCol; col <= _endCol; col++)
if ((Preview.Children[zoneIndex] as GridZone).IsSelected)
{
if (Model.CellChildMap[row, col] != mergedIndex)
{
// selection is more than one cell, merge is valid
MergePanel.Visibility = Visibility.Visible;
Canvas.SetTop(MergeButtons, mousePoint.Y);
Canvas.SetLeft(MergeButtons, mousePoint.X);
return;
}
selectedIndices.Add(zoneIndex);
}
}
// merge is only one zone. cancel merge;
ClearSelection();
if (selectedIndices.Count <= 1)
{
ClearSelection();
}
else
{
Point mousePoint = e.GetPosition(Preview);
MergePanel.Visibility = Visibility.Visible;
Canvas.SetLeft(MergeButtons, mousePoint.X);
Canvas.SetTop(MergeButtons, mousePoint.Y);
}
}
private bool _inMergeDrag;
private Point _mergeDragStart;
private void OnMergeDrag(object o, MouseEventArgs e)
{
if (_startDragPos.X == -1)
Point dragPosition = e.GetPosition(Preview);
Size actualSize = WorkAreaSize();
if (!_inMergeDrag)
{
_startDragPos = e.GetPosition(Preview);
_inMergeDrag = true;
_mergeDragStart = dragPosition;
}
GridLayoutModel model = Model;
// Find the new zone, if any
int dataLowX = Convert.ToInt32(Math.Min(_mergeDragStart.X, dragPosition.X) / actualSize.Width * GridData.Multiplier);
int dataHighX = Convert.ToInt32(Math.Max(_mergeDragStart.X, dragPosition.X) / actualSize.Width * GridData.Multiplier);
int dataLowY = Convert.ToInt32(Math.Min(_mergeDragStart.Y, dragPosition.Y) / actualSize.Height * GridData.Multiplier);
int dataHighY = Convert.ToInt32(Math.Max(_mergeDragStart.Y, dragPosition.Y) / actualSize.Height * GridData.Multiplier);
if (_startDragPos.X != -1)
var selectedIndices = new List<int>();
for (int zoneIndex = 0; zoneIndex < _data.Zones.Count(); zoneIndex++)
{
Point dragPos = e.GetPosition(Preview);
_startRow = -1;
_endRow = -1;
_startCol = -1;
_endCol = -1;
var zoneData = _data.Zones[zoneIndex];
int rows = model.Rows;
int cols = model.Columns;
bool selected = Math.Max(zoneData.Left, dataLowX) <= Math.Min(zoneData.Right, dataHighX) &&
Math.Max(zoneData.Top, dataLowY) <= Math.Min(zoneData.Bottom, dataHighY);
double minX, maxX;
if (dragPos.X < _startDragPos.X)
// Check whether the zone intersects the selected rectangle
(Preview.Children[zoneIndex] as GridZone).IsSelected = selected;
if (selected)
{
minX = dragPos.X;
maxX = _startDragPos.X;
selectedIndices.Add(zoneIndex);
}
else
{
minX = _startDragPos.X;
maxX = dragPos.X;
}
double minY, maxY;
if (dragPos.Y < _startDragPos.Y)
{
minY = dragPos.Y;
maxY = _startDragPos.Y;
}
else
{
minY = _startDragPos.Y;
maxY = dragPos.Y;
}
for (int row = 0; row < rows; row++)
{
if (_startRow == -1)
{
if (_data.RowEnd(row) > minY)
{
_startRow = row;
}
}
else if (_data.RowStart(row) > maxY)
{
_endRow = row - 1;
break;
}
}
if ((_startRow >= 0) && (_endRow == -1))
{
_endRow = rows - 1;
}
for (int col = 0; col < cols; col++)
{
if (_startCol == -1)
{
if (_data.ColumnBottom(col) > minX)
{
_startCol = col;
}
}
else if (_data.ColumnTop(col) > maxX)
{
_endCol = col - 1;
break;
}
}
if ((_startCol >= 0) && (_endCol == -1))
{
_endCol = cols - 1;
}
ExtendRangeToHaveEvenCellEdges();
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
((GridZone)Preview.Children[model.CellChildMap[row, col]]).IsSelected = (row >= _startRow) && (row <= _endRow) && (col >= _startCol) && (col <= _endCol);
}
}
e.Handled = true;
}
OnPreviewMouseMove(e);
// Compute the closure
_data.MergeClosureIndices(selectedIndices).ForEach((zoneIndex) =>
{
(Preview.Children[zoneIndex] as GridZone).IsSelected = true;
});
}
private void ClearSelection()
@@ -583,22 +408,27 @@ namespace FancyZonesEditor
{
((GridZone)zone).IsSelected = false;
}
_inMergeDrag = false;
}
private void MergeClick(object sender, RoutedEventArgs e)
{
MergePanel.Visibility = Visibility.Collapsed;
Action<int> deleteAction = (index) =>
{
DeleteZone(index);
};
_data.MergeZones(_startRow, _endRow, _startCol, _endCol, deleteAction, Preview.Children.Count);
_dragHandles.RemoveDragHandles();
_dragHandles.InitDragHandles(Model);
var selectedIndices = new List<int>();
for (int zoneIndex = 0; zoneIndex < _data.Zones.Count(); zoneIndex++)
{
if ((Preview.Children[zoneIndex] as GridZone).IsSelected)
{
selectedIndices.Add(zoneIndex);
}
}
OnGridDimensionsChanged();
ClearSelection();
_data.DoMerge(selectedIndices);
SetupUI();
}
private void MergeCancelClick(object sender, RoutedEventArgs e)
@@ -615,17 +445,9 @@ namespace FancyZonesEditor
protected override Size ArrangeOverride(Size arrangeBounds)
{
Size returnSize = base.ArrangeOverride(arrangeBounds);
ArrangeGridRects(arrangeBounds);
SetupUI();
return returnSize;
}
private GridData _data;
private GridDragHandles _dragHandles;
private int _startRow = -1;
private int _endRow = -1;
private int _startCol = -1;
private int _endCol = -1;
}
}

View File

@@ -35,12 +35,23 @@
Margin="0,4,0,0" />
</Grid>
<StackPanel Margin="16">
<TextBlock Text="{x:Static props:Resources.Note_Custom_Table}"
Foreground="{DynamicResource SecondaryForegroundBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Margin="0,8,0,0"
TextWrapping="Wrap" />
<StackPanel Margin="0,8,0,0">
<TextBlock
TextWrapping="Wrap">
<Run
FontWeight="Bold"
Text="{x:Static props:Resources.SplitterName}" />
<Run Text="{x:Static props:Resources.SplitterDescription}" />
</TextBlock>
<TextBlock
Margin="0,8,0,0"
TextWrapping="Wrap">
<Run
FontWeight="Bold"
Text="{x:Static props:Resources.MergeName}" />
<Run Text="{x:Static props:Resources.MergeDescription}" />
</TextBlock>
</StackPanel>
<Grid Margin="0,24,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>

View File

@@ -17,13 +17,13 @@ namespace FancyZonesEditor
{
private static readonly RotateTransform _rotateTransform = new RotateTransform(90, 24, 24);
public int StartRow { get; set; }
public int LeftReferenceZone { get; set; }
public int EndRow { get; set; }
public int RightReferenceZone { get; set; }
public int StartCol { get; set; }
public int TopReferenceZone { get; set; }
public int EndCol { get; set; }
public int BottomReferenceZone { get; set; }
public LayoutModel Model { get; set; }

View File

@@ -10,11 +10,10 @@
d:DesignHeight="450"
d:DesignWidth="800"
Background="{DynamicResource GridZoneBackgroundBrush}"
BorderBrush="{DynamicResource LayoutPreviewZoneBorderBrush}"
BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}"
BorderThickness="1"
Opacity="1"
ui:ControlHelper.CornerRadius="4"
Effect="{StaticResource ZoneDropShadow}"
mc:Ignorable="d">
<Grid x:Name="Frame">

View File

@@ -20,28 +20,29 @@ namespace FancyZonesEditor
// Non-localizable strings
private const string ObjectDependencyID = "IsSelected";
private const string GridZoneBackgroundBrushID = "GridZoneBackgroundBrush";
private const string PropertyIsShiftKeyPressedID = "IsShiftKeyPressed";
private const string SecondaryForegroundBrushID = "SecondaryForegroundBrush";
private const string AccentColorBrushID = "SystemControlBackgroundAccentBrush";
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(ObjectDependencyID, typeof(bool), typeof(GridZone), new PropertyMetadata(false, OnSelectionChanged));
public event SplitEventHandler Split;
public event SplitEventHandler FullSplit;
public event MouseEventHandler MergeDrag;
public event MouseButtonEventHandler MergeComplete;
public double[] VerticalSnapPoints { get; set; }
public double[] HorizontalSnapPoints { get; set; }
private readonly Rectangle _splitter;
private bool _switchOrientation;
private bool _switchOrientation = false;
private Point _lastPos = new Point(-1, -1);
private int _snappedPositionX;
private int _snappedPositionY;
private Point _mouseDownPos = new Point(-1, -1);
private bool _inMergeDrag;
private Orientation _splitOrientation;
private MagneticSnap _snapX;
private MagneticSnap _snapY;
private Func<Orientation, int, bool> _canSplit;
private bool _hovering;
private GridData.Zone _zone;
private static void OnSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
@@ -50,7 +51,7 @@ namespace FancyZonesEditor
private void OnSelectionChanged()
{
Background = IsSelected ? SystemParameters.WindowGlassBrush : App.Current.Resources[GridZoneBackgroundBrushID] as SolidColorBrush;
Background = IsSelected ? Application.Current.Resources[AccentColorBrushID] as SolidColorBrush : Application.Current.Resources[GridZoneBackgroundBrushID] as SolidColorBrush;
}
public bool IsSelected
@@ -59,21 +60,24 @@ namespace FancyZonesEditor
set { SetValue(IsSelectedProperty, value); }
}
public GridZone(int spacing)
public GridZone(int spacing, MagneticSnap snapX, MagneticSnap snapY, Func<Orientation, int, bool> canSplit, GridData.Zone zone)
{
InitializeComponent();
OnSelectionChanged();
_splitter = new Rectangle
{
Fill = SystemParameters.WindowGlassBrush,
Fill = Application.Current.Resources[AccentColorBrushID] as SolidColorBrush,
};
Body.Children.Add(_splitter);
Spacing = spacing;
SplitterThickness = Math.Max(spacing, 1);
((App)Application.Current).MainWindowSettings.PropertyChanged += ZoneSettings_PropertyChanged;
SizeChanged += GridZone_SizeChanged;
_snapX = snapX;
_snapY = snapY;
_canSplit = canSplit;
_zone = zone;
}
private void GridZone_SizeChanged(object sender, SizeChangedEventArgs e)
@@ -82,91 +86,76 @@ namespace FancyZonesEditor
HeightLabel.Text = Math.Round(ActualHeight).ToString();
}
private void ZoneSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
public void UpdateShiftState(bool shiftState)
{
if (e.PropertyName == PropertyIsShiftKeyPressedID)
{
_switchOrientation = ((App)Application.Current).MainWindowSettings.IsShiftKeyPressed;
if (_lastPos.X != -1)
{
UpdateSplitter();
}
}
}
_switchOrientation = shiftState;
protected override Size ArrangeOverride(Size size)
{
_splitOrientation = (size.Width > size.Height) ? Orientation.Vertical : Orientation.Horizontal;
return base.ArrangeOverride(size);
if (_lastPos.X != -1)
{
UpdateSplitter();
}
}
private bool IsVerticalSplit
{
get
{
bool isVertical = _splitOrientation == Orientation.Vertical;
if (_switchOrientation)
{
isVertical = !isVertical;
}
return isVertical;
}
get => (ActualWidth > ActualHeight) ^ _switchOrientation;
}
private int Spacing { get; set; }
private int SplitterThickness { get; set; }
private void UpdateSplitter()
{
if (!_hovering)
{
_splitter.Fill = Brushes.Transparent;
return;
}
bool enabled;
if (IsVerticalSplit)
{
double bodyWidth = Body.ActualWidth;
double pos = _lastPos.X - (SplitterThickness / 2);
if (pos < 0)
{
pos = 0;
}
else if (pos > (bodyWidth - SplitterThickness))
{
pos = bodyWidth - SplitterThickness;
}
double pos = _snapX.DataToPixelWithoutSnapping(_snappedPositionX) - Canvas.GetLeft(this) - (SplitterThickness / 2);
pos = Math.Clamp(pos, 0, bodyWidth - SplitterThickness);
Canvas.SetLeft(_splitter, pos);
Canvas.SetTop(_splitter, 0);
_splitter.MinWidth = SplitterThickness;
_splitter.MinHeight = Body.ActualHeight;
enabled = _canSplit(Orientation.Vertical, _snappedPositionX);
}
else
{
double bodyHeight = Body.ActualHeight;
double pos = _lastPos.Y - (SplitterThickness / 2);
if (pos < 0)
{
pos = 0;
}
else if (pos > (bodyHeight - SplitterThickness))
{
pos = bodyHeight - SplitterThickness;
}
double pos = _snapY.DataToPixelWithoutSnapping(_snappedPositionY) - Canvas.GetTop(this) - (SplitterThickness / 2);
pos = Math.Clamp(pos, 0, bodyHeight - SplitterThickness);
Canvas.SetLeft(_splitter, 0);
Canvas.SetTop(_splitter, pos);
_splitter.MinWidth = Body.ActualWidth;
_splitter.MinHeight = SplitterThickness;
enabled = _canSplit(Orientation.Horizontal, _snappedPositionY);
}
Brush disabledBrush = Application.Current.Resources[SecondaryForegroundBrushID] as SolidColorBrush;
Brush enabledBrush = Application.Current.Resources[AccentColorBrushID] as SolidColorBrush;
_splitter.Fill = enabled ? enabledBrush : disabledBrush;
}
protected override void OnMouseEnter(MouseEventArgs e)
{
_splitter.Fill = SystemParameters.WindowGlassBrush; // Active Accent color
base.OnMouseEnter(e);
_hovering = true;
UpdateSplitter();
_splitter.Fill = Application.Current.Resources[AccentColorBrushID] as SolidColorBrush;
}
protected override void OnMouseLeave(MouseEventArgs e)
{
_splitter.Fill = Brushes.Transparent;
_hovering = false;
UpdateSplitter();
base.OnMouseLeave(e);
}
@@ -185,38 +174,8 @@ namespace FancyZonesEditor
else
{
_lastPos = e.GetPosition(Body);
if (IsVerticalSplit)
{
if (VerticalSnapPoints != null)
{
int thickness = SplitterThickness;
foreach (double snapPoint in VerticalSnapPoints)
{
if (Math.Abs(_lastPos.X - snapPoint) <= (thickness * 2))
{
_lastPos.X = snapPoint;
break;
}
}
}
}
else
{
// horizontal split
if (HorizontalSnapPoints != null)
{
int thickness = SplitterThickness;
foreach (double snapPoint in HorizontalSnapPoints)
{
if (Math.Abs(_lastPos.Y - snapPoint) <= (thickness * 2))
{
_lastPos.Y = snapPoint;
break;
}
}
}
}
_snappedPositionX = _snapX.PixelToDataWithSnapping(e.GetPosition(Parent as GridEditor).X, _zone.Left, _zone.Right);
_snappedPositionY = _snapY.PixelToDataWithSnapping(e.GetPosition(Parent as GridEditor).Y, _zone.Top, _zone.Bottom);
if (_mouseDownPos.X == -1)
{
@@ -257,11 +216,11 @@ namespace FancyZonesEditor
{
if (IsVerticalSplit)
{
DoSplit(Orientation.Vertical, _lastPos.X - (thickness / 2));
DoSplit(Orientation.Vertical, _snappedPositionX);
}
else
{
DoSplit(Orientation.Horizontal, _lastPos.Y - (thickness / 2));
DoSplit(Orientation.Horizontal, _snappedPositionY);
}
}
}
@@ -280,19 +239,9 @@ namespace FancyZonesEditor
MergeComplete?.Invoke(this, e);
}
private void DoSplit(Orientation orientation, double offset)
private void DoSplit(Orientation orientation, int offset)
{
Split?.Invoke(this, new SplitEventArgs(orientation, offset, Spacing));
}
private void FullSplit_Click(object sender, RoutedEventArgs e)
{
DoFullSplit();
}
private void DoFullSplit()
{
FullSplit?.Invoke(this, new SplitEventArgs());
Split?.Invoke(this, new SplitEventArgs(orientation, offset));
}
}
}

View File

@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
@@ -27,7 +26,6 @@ namespace FancyZonesEditor
public static readonly DependencyProperty IsActualSizeProperty = DependencyProperty.Register(ObjectDependencyID, typeof(bool), typeof(LayoutPreview), new PropertyMetadata(false));
private LayoutModel _model;
private List<Int32Rect> _zones = new List<Int32Rect>();
public bool IsActualSize
{
@@ -82,11 +80,6 @@ namespace FancyZonesEditor
RenderPreview();
}
public Int32Rect[] GetZoneRects()
{
return _zones.ToArray();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
_model = (LayoutModel)DataContext;
@@ -105,8 +98,6 @@ namespace FancyZonesEditor
Body.RowDefinitions.Clear();
Body.ColumnDefinitions.Clear();
_zones.Clear();
if (_model is GridLayoutModel gridModel)
{
RenderGridPreview(gridModel);
@@ -121,32 +112,12 @@ namespace FancyZonesEditor
{
int rows = grid.Rows;
int cols = grid.Columns;
double spacing = grid.ShowSpacing ? grid.Spacing : 0;
RowColInfo[] rowInfo = (from percent in grid.RowPercents
select new RowColInfo(percent)).ToArray();
RowColInfo[] colInfo = (from percent in grid.ColumnPercents
select new RowColInfo(percent)).ToArray();
int spacing = grid.ShowSpacing ? grid.Spacing : 0;
var rowData = GridData.PrefixSum(grid.RowPercents);
var columnData = GridData.PrefixSum(grid.ColumnPercents);
var workArea = App.Overlay.WorkArea;
double width = workArea.Width - (spacing * (cols + 1));
double height = workArea.Height - (spacing * (rows + 1));
double top = spacing;
for (int row = 0; row < rows; row++)
{
double cellHeight = rowInfo[row].Recalculate(top, height);
top += cellHeight + spacing;
}
double left = spacing;
for (int col = 0; col < cols; col++)
{
double cellWidth = colInfo[col].Recalculate(left, width);
left += cellWidth + spacing;
}
Viewbox viewbox = new Viewbox
{
@@ -170,10 +141,8 @@ namespace FancyZonesEditor
{
// this is not a continuation of a span
Border rect = new Border();
left = colInfo[col].Start;
top = rowInfo[row].Start;
Canvas.SetTop(rect, top);
Canvas.SetLeft(rect, left);
double left = columnData[col] * workArea.Width / GridData.Multiplier;
double top = rowData[row] * workArea.Height / GridData.Multiplier;
int maxRow = row;
while (((maxRow + 1) < rows) && (grid.CellChildMap[maxRow + 1, col] == childIndex))
@@ -187,12 +156,21 @@ namespace FancyZonesEditor
maxCol++;
}
rect.Width = Math.Max(0, colInfo[maxCol].End - left);
rect.Height = Math.Max(0, rowInfo[maxRow].End - top);
double right = columnData[maxCol + 1] * workArea.Width / GridData.Multiplier;
double bottom = rowData[maxRow + 1] * workArea.Height / GridData.Multiplier;
left += col == 0 ? spacing : spacing / 2;
right -= maxCol == cols - 1 ? spacing : spacing / 2;
top += row == 0 ? spacing : spacing / 2;
bottom -= maxRow == rows - 1 ? spacing : spacing / 2;
Canvas.SetTop(rect, top);
Canvas.SetLeft(rect, left);
rect.Width = Math.Max(1, right - left);
rect.Height = Math.Max(1, bottom - top);
rect.Style = (Style)FindResource("GridLayoutActualScalePreviewStyle");
frame.Children.Add(rect);
_zones.Add(new Int32Rect(
(int)left, (int)top, (int)rect.Width, (int)rect.Height));
}
}
}
@@ -283,7 +261,7 @@ namespace FancyZonesEditor
private void RenderCanvasPreview(CanvasLayoutModel canvas)
{
var workArea = canvas.CanvasRect;
if (workArea.Width == 0 || workArea.Height == 0 || App.Overlay.SpanZonesAcrossMonitors)
if (workArea.Width == 0 || workArea.Height == 0)
{
workArea = App.Overlay.WorkArea;
}

View File

@@ -0,0 +1,79 @@
// 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.Linq;
namespace FancyZonesEditor
{
public class MagneticSnap
{
private List<int> _keyPoints;
private double _workAreaSize;
private const int MagnetZoneMaxSize = GridData.Multiplier / 12;
public MagneticSnap(List<int> keyPoints, double workAreaSize)
{
_keyPoints = keyPoints;
_workAreaSize = workAreaSize;
}
public int PixelToDataWithSnapping(double pixel, int low, int high)
{
var keyPoints = _keyPoints.Where(x => low < x && x < high).ToList();
var magnetZoneSizes = new List<int>();
for (int i = 0; i < keyPoints.Count; i++)
{
int previous = i == 0 ? low : keyPoints[i - 1];
int next = i == keyPoints.Count - 1 ? high : keyPoints[i + 1];
magnetZoneSizes.Add(Math.Min(keyPoints[i] - previous, Math.Min(next - keyPoints[i], MagnetZoneMaxSize)) / 2);
}
int data = Convert.ToInt32(pixel / _workAreaSize * GridData.Multiplier);
data = Math.Clamp(data, low, high);
int result;
int snapId = -1;
for (int i = 0; i < keyPoints.Count; ++i)
{
if (Math.Abs(data - keyPoints[i]) <= magnetZoneSizes[i])
{
snapId = i;
break;
}
}
if (snapId == -1)
{
result = data;
}
else
{
int deadZoneWidth = (magnetZoneSizes[snapId] + 1) / 2;
if (Math.Abs(data - keyPoints[snapId]) <= deadZoneWidth)
{
result = keyPoints[snapId];
}
else if (data < keyPoints[snapId])
{
result = data + (data - (keyPoints[snapId] - magnetZoneSizes[snapId]));
}
else
{
result = data - ((keyPoints[snapId] + magnetZoneSizes[snapId]) - data);
}
}
return Math.Clamp(result, low, high);
}
public double DataToPixelWithoutSnapping(int data)
{
return _workAreaSize * data / GridData.Multiplier;
}
}
}

View File

@@ -84,6 +84,9 @@
</StackPanel>
</ToolTip>
</Border.ToolTip>
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
<TextBlock Name="IndexText"
TextTrimming="CharacterEllipsis"
Text="{Binding Index}"
@@ -93,6 +96,17 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryForegroundBrush}" />
<TextBlock Name="ResolutionText"
TextTrimming="CharacterEllipsis"
Text="{Binding Dimensions}"
Grid.Row="0"
Margin="0,0,0,0"
FontSize="11"
FontWeight="SemiBold"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{DynamicResource SecondaryForegroundBrush}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Selected}"
@@ -100,6 +114,10 @@
<Setter TargetName="IndexText"
Property="Foreground"
Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
<Setter TargetName="ResolutionText"
Property="Foreground"
Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
<Setter TargetName="MonitorItem"
Property="BorderBrush"
Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
@@ -284,6 +302,7 @@
</Grid>
</ScrollViewer>
<Button x:Name="NewLayoutButton"
Click="NewLayoutButton_Click"
HorizontalAlignment="Right"
@@ -328,7 +347,6 @@
<local1:MonitorViewModel x:Name="monitorViewModel" />
</ScrollViewer.DataContext>
<Grid>
<ItemsControl x:Name="MainWindowItemControl"
TabIndex="0"
ItemTemplate="{StaticResource MonitorItemTemplate}"
@@ -341,7 +359,6 @@
Margin="8, 0, 8, 16" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</ScrollViewer>
@@ -355,6 +372,7 @@
PrimaryButtonClick="EditLayoutDialog_PrimaryButtonClick"
SecondaryButtonClick="EditLayoutDialog_SecondaryButtonClick"
Title="{x:Static props:Resources.Edit_Layout}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Opened="Dialog_Opened"
Closed="Dialog_Closed">
<Grid DataContext="{Binding SelectedModel}"
@@ -419,55 +437,55 @@
HorizontalAlignment="Center"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeTemplateToVisibilityConverter}}">
<Button x:Name="decrementZones"
Width="40"
Height="40"
AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Decrement}"
ToolTip="{x:Static props:Resources.Zone_Count_Decrement}"
Foreground="{DynamicResource SystemControlBackgroundAccentBrush}"
Style="{StaticResource IconOnlyButtonStyle}"
Click="DecrementZones_Click">
<Button.Content>
<TextBlock Text="&#xE108;"
FontFamily="Segoe MDL2 Assets" />
</Button.Content>
</Button>
<TextBlock x:Name="zoneCount"
Text="{Binding TemplateZoneCount}"
FontWeight="SemiBold"
FontSize="18"
Width="32"
HorizontalAlignment="Center"
TextAlignment="Center"
Margin="0,-4,0,0"
ToolTip="Number of zones"
VerticalAlignment="Center" />
<Button x:Name="decrementZones"
Width="40"
Height="40"
AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Decrement}"
ToolTip="{x:Static props:Resources.Zone_Count_Decrement}"
Foreground="{DynamicResource SystemControlBackgroundAccentBrush}"
Style="{StaticResource IconOnlyButtonStyle}"
Click="DecrementZones_Click">
<Button.Content>
<TextBlock Text="&#xE108;"
FontFamily="Segoe MDL2 Assets" />
</Button.Content>
</Button>
<Button x:Name="incrementZones"
Width="40"
Height="40"
AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Increment}"
ToolTip="{x:Static props:Resources.Zone_Count_Increment}"
Foreground="{DynamicResource SystemControlBackgroundAccentBrush}"
Style="{StaticResource IconOnlyButtonStyle}"
Click="IncrementZones_Click">
<Button.Content>
<TextBlock Text="&#xE109;"
FontFamily="Segoe MDL2 Assets" />
</Button.Content>
</Button>
</StackPanel>
<TextBlock x:Name="zoneCount"
Text="{Binding TemplateZoneCount}"
FontWeight="SemiBold"
FontSize="18"
Width="32"
HorizontalAlignment="Center"
TextAlignment="Center"
Margin="0,-4,0,0"
ToolTip="Number of zones"
VerticalAlignment="Center" />
<Button x:Name="incrementZones"
Width="40"
Height="40"
AutomationProperties.Name="{x:Static props:Resources.Zone_Count_Increment}"
ToolTip="{x:Static props:Resources.Zone_Count_Increment}"
Foreground="{DynamicResource SystemControlBackgroundAccentBrush}"
Style="{StaticResource IconOnlyButtonStyle}"
Click="IncrementZones_Click">
<Button.Content>
<TextBlock Text="&#xE109;"
FontFamily="Segoe MDL2 Assets" />
</Button.Content>
</Button>
</StackPanel>
<TextBox Text="{Binding Name}"
ui:ControlHelper.Header="{x:Static props:Resources.Name}"
Margin="0,16,0,0"
Margin="0,16,2,0"
Visibility="{Binding IsCustom, Converter={StaticResource BooleanToVisibilityConverter}}"
HorizontalAlignment="Stretch" />
<CheckBox x:Name="spaceAroundSetting"
Content="{x:Static props:Resources.Show_Space_Zones}"
IsChecked="{Binding ShowSpacing}"
Margin="0,16,0,0"
Margin="0,16,12,0"
Visibility="{Binding Converter={StaticResource LayoutModelTypeToVisibilityConverter}}" />
<ui:NumberBox Margin="0,6,0,0"
@@ -483,7 +501,8 @@
<TextBlock Text="{x:Static props:Resources.Distance_adjacent_zones}"
IsEnabled="{Binding ShowSpacing}"
Margin="0,16,0,0"
Margin="0,16,12,0"
TextWrapping="Wrap"
Foreground="{DynamicResource PrimaryForegroundBrush}"
x:Name="sensitivityRadiusValue" />
@@ -495,6 +514,18 @@
SpinButtonPlacementMode="Compact"
HorizontalAlignment="Left"
AutomationProperties.LabeledBy="{Binding ElementName=sensitivityRadiusValue}" />
<TextBlock Text="{x:Static props:Resources.QuickKey_Select}"
Margin="0,16,12,0"
Foreground="{DynamicResource PrimaryForegroundBrush}"
TextWrapping="Wrap"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeCustomToVisibilityConverter}}"/>
<ComboBox x:Name="quickKeySelectionComboBox"
Margin="0,6,0,0"
ItemsSource="{Binding QuickKeysAvailable}"
SelectedItem="{Binding QuickKey}"
Width="106"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeCustomToVisibilityConverter}}"/>
</StackPanel>
</Grid>
</ui:ContentDialog>

View File

@@ -304,7 +304,8 @@ namespace FancyZonesEditor
}
else
{
CanvasLayoutModel canvasModel = new CanvasLayoutModel(LayoutNameText.Text, LayoutType.Custom);
var area = App.Overlay.WorkArea;
CanvasLayoutModel canvasModel = new CanvasLayoutModel(LayoutNameText.Text, LayoutType.Custom, (int)area.Width, (int)area.Height);
canvasModel.AddZone();
selectedLayoutModel = canvasModel;
}

View File

@@ -36,6 +36,12 @@ namespace FancyZonesEditor.Models
CanvasRect = new Rect(new Size(width, height));
}
public CanvasLayoutModel(string name, LayoutType type, int width, int height)
: base(name, type)
{
CanvasRect = new Rect(new Size(width, height));
}
public CanvasLayoutModel(string name, LayoutType type)
: base(name, type)
{
@@ -148,6 +154,7 @@ namespace FancyZonesEditor.Models
}
layout.SensitivityRadius = SensitivityRadius;
layout.CanvasRect = CanvasRect;
return layout;
}
@@ -161,6 +168,7 @@ namespace FancyZonesEditor.Models
other._topLeft = _topLeft;
other.SensitivityRadius = SensitivityRadius;
other.CanvasRect = CanvasRect;
other.UpdateLayout();
}

View File

@@ -120,11 +120,6 @@ namespace FancyZonesEditor.Models
private int _spacing = LayoutSettings.DefaultSpacing;
// FreeZones (not persisted) - used to keep track of child indices that are no longer in use in the CellChildMap,
// making them candidates for re-use when it's needed to add another child
// TODO: do I need FreeZones on the data model? - I think I do
public IList<int> FreeZones { get; } = new List<int>();
public GridLayoutModel()
: base()
{

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
@@ -16,6 +17,8 @@ namespace FancyZonesEditor.Models
{
_guid = Guid.NewGuid();
Type = LayoutType.Custom;
MainWindowSettingsModel.QuickKeys.PropertyChanged += QuickSwitchKeys_PropertyChanged;
}
protected LayoutModel(string name)
@@ -48,6 +51,9 @@ namespace FancyZonesEditor.Models
_isApplied = other._isApplied;
_sensitivityRadius = other._sensitivityRadius;
_zoneCount = other._zoneCount;
_quickKey = other._quickKey;
MainWindowSettingsModel.QuickKeys.PropertyChanged += QuickSwitchKeys_PropertyChanged;
}
// Name - the display name for this layout model - is also used as the key in the registry
@@ -158,6 +164,55 @@ namespace FancyZonesEditor.Models
private int _sensitivityRadius = LayoutSettings.DefaultSensitivityRadius;
public List<string> QuickKeysAvailable
{
get
{
List<string> result = new List<string>();
foreach (var pair in MainWindowSettingsModel.QuickKeys.SelectedKeys)
{
if (pair.Value == string.Empty || pair.Value == Uuid)
{
result.Add(pair.Key);
}
}
return result;
}
}
public string QuickKey
{
get
{
return _quickKey == -1 ? Properties.Resources.Quick_Key_None : _quickKey.ToString();
}
set
{
string none = Properties.Resources.Quick_Key_None;
var intValue = value == none ? -1 : int.Parse(value);
if (intValue != _quickKey)
{
string prev = _quickKey == -1 ? none : _quickKey.ToString();
_quickKey = intValue;
if (intValue != -1)
{
MainWindowSettingsModel.QuickKeys.SelectKey(value, Uuid);
}
else
{
MainWindowSettingsModel.QuickKeys.FreeKey(prev);
}
FirePropertyChanged(nameof(QuickKey));
}
}
}
private int _quickKey = -1;
// TemplateZoneCount - number of zones selected in the picker window for template layouts
public int TemplateZoneCount
{
@@ -200,6 +255,11 @@ namespace FancyZonesEditor.Models
// Removes this Layout from the registry and the loaded CustomModels list
public void Delete()
{
if (_quickKey != -1)
{
MainWindowSettingsModel.QuickKeys.FreeKey(QuickKey);
}
var customModels = MainWindowSettingsModel.CustomModels;
int i = customModels.IndexOf(this);
if (i != -1)
@@ -241,5 +301,17 @@ namespace FancyZonesEditor.Models
{
PersistData();
}
private void QuickSwitchKeys_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
foreach (var pair in MainWindowSettingsModel.QuickKeys.SelectedKeys)
{
if (pair.Value == Uuid)
{
QuickKey = pair.Key.ToString();
break;
}
}
}
}
}

View File

@@ -6,7 +6,7 @@ namespace FancyZonesEditor.Models
{
public enum LayoutType
{
Blank = -1,
Blank = 0,
Focus,
Columns,
Rows,

View File

@@ -23,13 +23,6 @@ namespace FancyZonesEditor
VirtualDesktopId,
}
private readonly CanvasLayoutModel _blankModel;
private readonly CanvasLayoutModel _focusModel;
private readonly GridLayoutModel _rowsModel;
private readonly GridLayoutModel _columnsModel;
private readonly GridLayoutModel _gridModel;
private readonly GridLayoutModel _priorityGridModel;
// Non-localizable strings
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
@@ -53,40 +46,40 @@ namespace FancyZonesEditor
public MainWindowSettingsModel()
{
// Initialize default layout models: Blank, Focus, Columns, Rows, Grid, and PriorityGrid
_blankModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Blank, LayoutType.Blank)
var blankModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Blank, LayoutType.Blank)
{
TemplateZoneCount = 0,
SensitivityRadius = 0,
};
DefaultModels.Add(_blankModel);
DefaultModels.Insert((int)LayoutType.Blank, blankModel);
_focusModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Focus, LayoutType.Focus);
_focusModel.InitTemplateZones();
DefaultModels.Add(_focusModel);
var focusModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Focus, LayoutType.Focus);
focusModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Focus, focusModel);
_columnsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Columns, LayoutType.Columns)
var columnsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Columns, LayoutType.Columns)
{
Rows = 1,
RowPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
};
_columnsModel.InitTemplateZones();
DefaultModels.Add(_columnsModel);
columnsModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Columns, columnsModel);
_rowsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Rows, LayoutType.Rows)
var rowsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Rows, LayoutType.Rows)
{
Columns = 1,
ColumnPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
};
_rowsModel.InitTemplateZones();
DefaultModels.Add(_rowsModel);
rowsModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Rows, rowsModel);
_gridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Grid, LayoutType.Grid);
_gridModel.InitTemplateZones();
DefaultModels.Add(_gridModel);
var gridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Grid, LayoutType.Grid);
gridModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Grid, gridModel);
_priorityGridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Priority_Grid, LayoutType.PriorityGrid);
_priorityGridModel.InitTemplateZones();
DefaultModels.Add(_priorityGridModel);
var priorityGridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Priority_Grid, LayoutType.PriorityGrid);
priorityGridModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.PriorityGrid, priorityGridModel);
}
// IsShiftKeyPressed - is the shift key currently being held down
@@ -133,7 +126,7 @@ namespace FancyZonesEditor
{
get
{
return _blankModel;
return DefaultModels[(int)LayoutType.Blank];
}
}
@@ -149,6 +142,8 @@ namespace FancyZonesEditor
private static ObservableCollection<LayoutModel> _customModels = new ObservableCollection<LayoutModel>();
public static QuickKeysModel QuickKeys { get; } = new QuickKeysModel();
public LayoutModel SelectedModel
{
get
@@ -202,7 +197,7 @@ namespace FancyZonesEditor
{
foreach (LayoutModel model in CustomModels)
{
if ("{" + model.Guid.ToString().ToUpperInvariant() + "}" == currentApplied.ZonesetUuid.ToUpperInvariant())
if (model.Uuid == currentApplied.ZonesetUuid.ToUpperInvariant())
{
// found match
foundModel = model;
@@ -234,7 +229,7 @@ namespace FancyZonesEditor
if (foundModel == null)
{
foundModel = _priorityGridModel;
foundModel = DefaultModels[(int)LayoutType.PriorityGrid];
}
SetSelectedModel(foundModel);
@@ -255,6 +250,7 @@ namespace FancyZonesEditor
SelectedModel.IsSelected = model.IsSelected;
SelectedModel.IsApplied = model.IsApplied;
SelectedModel.Name = model.Name;
SelectedModel.QuickKey = model.QuickKey;
if (model is GridLayoutModel grid)
{

View File

@@ -0,0 +1,87 @@
// 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 System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
namespace FancyZonesEditor.Models
{
public class QuickKeysModel : INotifyPropertyChanged
{
public SortedDictionary<string, string> SelectedKeys { get; } = new SortedDictionary<string, string>()
{
{ Properties.Resources.Quick_Key_None, string.Empty },
{ "0", string.Empty },
{ "1", string.Empty },
{ "2", string.Empty },
{ "3", string.Empty },
{ "4", string.Empty },
{ "5", string.Empty },
{ "6", string.Empty },
{ "7", string.Empty },
{ "8", string.Empty },
{ "9", string.Empty },
};
public QuickKeysModel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
public void FreeKey(string key)
{
if (SelectedKeys.ContainsKey(key))
{
SelectedKeys[key] = string.Empty;
FirePropertyChanged();
}
}
public bool SelectKey(string key, string uuid)
{
if (!SelectedKeys.ContainsKey(key))
{
return false;
}
if (SelectedKeys[key] == uuid)
{
return true;
}
// clean previous value
foreach (var pair in SelectedKeys)
{
if (pair.Value == uuid)
{
SelectedKeys[pair.Key] = string.Empty;
break;
}
}
SelectedKeys[key] = uuid;
FirePropertyChanged();
return true;
}
public void CleanUp()
{
var keys = SelectedKeys.Keys.ToList();
foreach (var key in keys)
{
SelectedKeys[key] = string.Empty;
}
FirePropertyChanged();
}
protected virtual void FirePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -363,46 +363,5 @@ namespace FancyZonesEditor
Monitors.Add(monitor);
}
}
public Int32Rect[] GetZoneRects()
{
if (_editor != null)
{
if (_editor is GridEditor gridEditor)
{
return ZoneRectsFromPanel(gridEditor.PreviewPanel);
}
else
{
// CanvasEditor
return ZoneRectsFromPanel(((CanvasEditor)_editor).Preview);
}
}
else
{
// One of the predefined zones (neither grid or canvas editor used).
return _layoutPreview.GetZoneRects();
}
}
private Int32Rect[] ZoneRectsFromPanel(Panel previewPanel)
{
// TODO: the ideal here is that the ArrangeRects logic is entirely inside the model, so we don't have to walk the UIElement children to get the rect info
int count = previewPanel.Children.Count;
Int32Rect[] zones = new Int32Rect[count];
for (int i = 0; i < count; i++)
{
FrameworkElement child = (FrameworkElement)previewPanel.Children[i];
Point topLeft = child.TransformToAncestor(previewPanel).Transform(default);
zones[i].X = (int)topLeft.X;
zones[i].Y = (int)topLeft.Y;
zones[i].Width = (int)child.ActualWidth;
zones[i].Height = (int)child.ActualHeight;
}
return zones;
}
}
}

View File

@@ -376,11 +376,11 @@ namespace FancyZonesEditor.Properties {
}
/// <summary>
/// Looks up a localized string similar to &apos;zones-settings.json&apos; contains malformed data..
/// Looks up a localized string similar to A layout that contained invalid data has been removed..
/// </summary>
public static string Error_Parsing_Zones_Settings_Malformed_Data {
public static string Error_Parsing_Zones_Settings_Message {
get {
return ResourceManager.GetString("Error_Parsing_Zones_Settings_Malformed_Data", resourceCulture);
return ResourceManager.GetString("Error_Parsing_Zones_Settings_Message", resourceCulture);
}
}
@@ -393,15 +393,6 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Would you like to continue? Malformed data will be lost..
/// </summary>
public static string Error_Parsing_Zones_Settings_User_Choice {
get {
return ResourceManager.GetString("Error_Parsing_Zones_Settings_User_Choice", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error persisting custom layout.
/// </summary>
@@ -501,6 +492,24 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Click and drag across zones..
/// </summary>
public static string MergeDescription {
get {
return ResourceManager.GetString("MergeDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Merge/Delete:.
/// </summary>
public static string MergeName {
get {
return ResourceManager.GetString("MergeName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Monitor.
/// </summary>
@@ -528,16 +537,6 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Hold down Shift key to change orientation of splitter.
///To merge zones, select the zones and click &quot;merge&quot;..
/// </summary>
public static string Note_Custom_Table {
get {
return ResourceManager.GetString("Note_Custom_Table", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Number of zones.
/// </summary>
@@ -547,6 +546,24 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to None.
/// </summary>
public static string Quick_Key_None {
get {
return ResourceManager.GetString("Quick_Key_None", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Select a key to quickly apply the layout (Win + Ctrl + Alt + key).
/// </summary>
public static string QuickKey_Select {
get {
return ResourceManager.GetString("QuickKey_Select", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reset layout.
/// </summary>
@@ -601,6 +618,24 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Hold Shift key for vertical split..
/// </summary>
public static string SplitterDescription {
get {
return ResourceManager.GetString("SplitterDescription", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Splitter:.
/// </summary>
public static string SplitterName {
get {
return ResourceManager.GetString("SplitterName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No layout.
/// </summary>

View File

@@ -159,10 +159,6 @@
<data name="Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="Note_Custom_Table" xml:space="preserve">
<value>Hold down Shift key to change orientation of splitter.
To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".</value>
</data>
<data name="Save_Apply" xml:space="preserve">
<value>Save &amp; apply</value>
</data>
@@ -307,14 +303,11 @@ To merge zones, hold down the left mouse button, drag the mouse to the zone to m
<value>Delete zone</value>
<comment>A tooltip on a button that allows the user to delete a zone</comment>
</data>
<data name="Error_Parsing_Zones_Settings_Malformed_Data" xml:space="preserve">
<value>'zones-settings.json' contains malformed data.</value>
</data>
<data name="Error_Parsing_Zones_Settings_Title" xml:space="preserve">
<value>Editor settings parsing error.</value>
</data>
<data name="Error_Parsing_Zones_Settings_User_Choice" xml:space="preserve">
<value>Would you like to continue? Malformed data will be lost.</value>
<data name="Error_Parsing_Zones_Settings_Message" xml:space="preserve">
<value>A layout that contained invalid data has been removed.</value>
</data>
<data name="Apply_Layout" xml:space="preserve">
<value>Apply layout</value>
@@ -334,4 +327,26 @@ To merge zones, hold down the left mouse button, drag the mouse to the zone to m
<data name="Default_Custom_Layout_Name" xml:space="preserve">
<value>Custom layout</value>
</data>
<data name="MergeDescription" xml:space="preserve">
<value>Click and drag across zones.</value>
<comment>Click mouse, hold button down and drag across multiple zones in tool</comment>
</data>
<data name="MergeName" xml:space="preserve">
<value>Merge/Delete:</value>
<comment>Title for concept behind Merging two zones together or removing an zone</comment>
</data>
<data name="SplitterDescription" xml:space="preserve">
<value>Hold Shift key for vertical split.</value>
<comment>A segmenter visual for splitting one item into two. This would be the vertical line. Shift key is referring to key on keyboard</comment>
</data>
<data name="SplitterName" xml:space="preserve">
<value>Splitter:</value>
<comment>Title for concept: A segmenter visual for splitting one item into two. This would be the vertical line</comment>
</data>
<data name="QuickKey_Select" xml:space="preserve">
<value>Select a key to quickly apply the layout (Win + Ctrl + Alt + key)</value>
</data>
<data name="Quick_Key_None" xml:space="preserve">
<value>None</value>
</data>
</root>

View File

@@ -1,66 +0,0 @@
// 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 FancyZonesEditor
{
public class RowColInfo
{
private const int _multiplier = 10000;
public double Extent { get; set; }
public double Start { get; set; }
public double End { get; set; }
public int Percent { get; set; }
public RowColInfo(int percent)
{
Percent = percent;
}
public RowColInfo(RowColInfo other)
{
Percent = other.Percent;
Extent = other.Extent;
Start = other.Start;
End = other.End;
}
public RowColInfo(int index, int count)
{
Percent = (_multiplier / count) + ((index == 0) ? (_multiplier % count) : 0);
}
public double Recalculate(double start, double totalExtent)
{
Start = start;
Extent = totalExtent * Percent / _multiplier;
End = Start + Extent;
return Extent;
}
public void RecalculatePercent(double newTotalExtent)
{
Percent = (int)(Extent * _multiplier / newTotalExtent);
}
public RowColInfo[] Split(double offset, double space)
{
RowColInfo[] info = new RowColInfo[2];
double totalExtent = Extent * _multiplier / Percent;
totalExtent -= space;
int percent0 = (int)(offset * _multiplier / totalExtent);
int percent1 = (int)((Extent - space - offset) * _multiplier / totalExtent);
info[0] = new RowColInfo(percent0);
info[1] = new RowColInfo(percent1);
return info;
}
}
}

View File

@@ -13,18 +13,15 @@ namespace FancyZonesEditor
{
}
public SplitEventArgs(Orientation orientation, double offset, double thickness)
public SplitEventArgs(Orientation orientation, int offset)
{
Orientation = orientation;
Offset = offset;
Space = thickness;
}
public Orientation Orientation { get; }
public double Offset { get; }
public double Space { get; }
public int Offset { get; }
}
public delegate void SplitEventHandler(object sender, SplitEventArgs args);

View File

@@ -14,10 +14,9 @@
<Style x:Key="GridLayoutActualScalePreviewStyle" TargetType="Border">
<Setter Property="Background" Value="{DynamicResource LayoutPreviewActualScaleZoneBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource LayoutPreviewZoneBorderBrush}" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="Effect" Value="{StaticResource ZoneDropShadow}" />
</Style>
<Style x:Key="CanvasLayoutSmallScalePreviewStyle" TargetType="Border">
@@ -29,9 +28,8 @@
<Style x:Key="CanvasLayoutActualScalePreviewStyle" TargetType="Border">
<Setter Property="Background" Value="{DynamicResource LayoutPreviewActualScaleZoneBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource LayoutPreviewZoneBorderBrush}" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="Effect" Value="{StaticResource ZoneDropShadow}" />
</Style>
</ResourceDictionary>

View File

@@ -15,15 +15,21 @@
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF9a9a9a" />
<SolidColorBrush x:Key="BackdropBrush" Color="#E55B5B5B" />
<SolidColorBrush x:Key="BackdropBrush" Color="#40F0F0F0" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
<!-- Used for layouts and monitors -->
<SolidColorBrush x:Key="LayoutItemBackgroundBrush" Color="#FF3A3A3A" />
<SolidColorBrush x:Key="LayoutItemBackgroundPointerOverBrush" Color="#FF333333" />
<!-- Used for the fullscreen preview (ActualScale) and for the thumbnail preview (SmallScale) -->
<SolidColorBrush x:Key="LayoutPreviewBackgroundBrush" Color="#331B1B1B" />
<!-- Currently unused -->
<SolidColorBrush x:Key="LayoutPreviewZoneBorderBrush" Color="#FF555454" />
<SolidColorBrush x:Key="LayoutPreviewActualScaleZoneBackgroundBrush" Color="#E5202020" />
<SolidColorBrush x:Key="LayoutPreviewActualScaleZoneBackgroundBrush" Color="#B0202020" />
<SolidColorBrush x:Key="LayoutPreviewSmallScaleZoneBackgroundBrush" Color="#FF202020" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#E5202020" />
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#E5202020" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#B0202020" />
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#B0202020" />
</ResourceDictionary>

View File

@@ -14,16 +14,22 @@
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FFeeeeee" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FFF3F3F3" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FF000000" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF949494" />
<SolidColorBrush x:Key="BackdropBrush" Color="#E5949494" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF6A6A6A" />
<SolidColorBrush x:Key="BackdropBrush" Color="#85F0F0F0" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF949494" />
<!-- Used for layouts and monitors -->
<SolidColorBrush x:Key="LayoutItemBackgroundBrush" Color="#FFffffff"/>
<SolidColorBrush x:Key="LayoutItemBackgroundPointerOverBrush" Color="#FFf2f2f2" />
<!-- Used for the fullscreen preview (ActualScale) and for the thumbnail preview (SmallScale) -->
<SolidColorBrush x:Key="LayoutPreviewBackgroundBrush" Color="#19949494" />
<!-- Currently unused -->
<SolidColorBrush x:Key="LayoutPreviewZoneBorderBrush" Color="#FFC3BEBE" />
<SolidColorBrush x:Key="LayoutPreviewActualScaleZoneBackgroundBrush" Color="#E5F3F3F3" />
<SolidColorBrush x:Key="LayoutPreviewActualScaleZoneBackgroundBrush" Color="#C5F8F8F8" />
<SolidColorBrush x:Key="LayoutPreviewSmallScaleZoneBackgroundBrush" Color="#FFDCDCDC" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#E5F3F3F3" />
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#E5F3F3F3" />
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#C5F8F8F8" />
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#C5F8F8F8" />
</ResourceDictionary>

View File

@@ -57,6 +57,7 @@ namespace FancyZonesEditor.Utils
MonitorTop,
}
// parsing cmd args
private struct NativeMonitorData
{
public string MonitorId { get; set; }
@@ -87,6 +88,7 @@ namespace FancyZonesEditor.Utils
}
}
// zones-settings: devices
private struct DeviceWrapper
{
public struct ActiveZoneSetWrapper
@@ -109,6 +111,7 @@ namespace FancyZonesEditor.Utils
public int EditorSensitivityRadius { get; set; }
}
// zones-settings: custom-zone-sets
private class CanvasInfoWrapper
{
public struct CanvasZoneWrapper
@@ -131,6 +134,7 @@ namespace FancyZonesEditor.Utils
public int SensitivityRadius { get; set; } = LayoutSettings.DefaultSensitivityRadius;
}
// zones-settings: custom-zone-sets
private class GridInfoWrapper
{
public int Rows { get; set; }
@@ -150,6 +154,7 @@ namespace FancyZonesEditor.Utils
public int SensitivityRadius { get; set; } = LayoutSettings.DefaultSensitivityRadius;
}
// zones-settings: custom-zone-sets
private struct CustomLayoutWrapper
{
public string Uuid { get; set; }
@@ -161,6 +166,7 @@ namespace FancyZonesEditor.Utils
public JsonElement Info { get; set; } // CanvasInfoWrapper or GridInfoWrapper
}
// zones-settings: templates
private struct TemplateLayoutWrapper
{
public string Type { get; set; }
@@ -174,6 +180,15 @@ namespace FancyZonesEditor.Utils
public int SensitivityRadius { get; set; }
}
// zones-settings: quick-layout-keys-wrapper
private struct QuickLayoutKeysWrapper
{
public int Key { get; set; }
public string Uuid { get; set; }
}
// zones-settings
private struct ZoneSettingsWrapper
{
public List<DeviceWrapper> Devices { get; set; }
@@ -181,6 +196,8 @@ namespace FancyZonesEditor.Utils
public List<CustomLayoutWrapper> CustomZoneSets { get; set; }
public List<TemplateLayoutWrapper> Templates { get; set; }
public List<QuickLayoutKeysWrapper> QuickLayoutKeys { get; set; }
}
private struct EditorParams
@@ -528,10 +545,11 @@ namespace FancyZonesEditor.Utils
bool devicesParsingResult = SetDevices(zoneSettings.Devices);
bool customZonesParsingResult = SetCustomLayouts(zoneSettings.CustomZoneSets);
bool templatesParsingResult = SetTemplateLayouts(zoneSettings.Templates);
bool quickLayoutSwitchKeysParsingResult = SetQuickLayoutSwitchKeys(zoneSettings.QuickLayoutKeys);
if (!devicesParsingResult || !customZonesParsingResult)
{
return new ParsingResult(false, Properties.Resources.Error_Parsing_Zones_Settings_Malformed_Data, settingsString);
return new ParsingResult(false, FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_Message, settingsString);
}
}
catch (Exception ex)
@@ -549,6 +567,7 @@ namespace FancyZonesEditor.Utils
zoneSettings.Devices = new List<DeviceWrapper>();
zoneSettings.CustomZoneSets = new List<CustomLayoutWrapper>();
zoneSettings.Templates = new List<TemplateLayoutWrapper>();
zoneSettings.QuickLayoutKeys = new List<QuickLayoutKeysWrapper>();
// Serialize used devices
foreach (var monitor in App.Overlay.Monitors)
@@ -685,6 +704,27 @@ namespace FancyZonesEditor.Utils
zoneSettings.Templates.Add(wrapper);
}
// Serialize quick layout switch keys
foreach (var pair in MainWindowSettingsModel.QuickKeys.SelectedKeys)
{
if (pair.Value != string.Empty)
{
try
{
QuickLayoutKeysWrapper wrapper = new QuickLayoutKeysWrapper
{
Key = int.Parse(pair.Key),
Uuid = pair.Value,
};
zoneSettings.QuickLayoutKeys.Add(wrapper);
}
catch (Exception)
{
}
}
}
try
{
string jsonString = JsonSerializer.Serialize(zoneSettings, _options);
@@ -770,39 +810,25 @@ namespace FancyZonesEditor.Utils
continue;
}
LayoutModel layout;
if (zoneSet.Type == CanvasLayoutModel.ModelTypeID)
LayoutModel layout = null;
try
{
var info = JsonSerializer.Deserialize<CanvasInfoWrapper>(zoneSet.Info.GetRawText(), _options);
var zones = new List<Int32Rect>();
foreach (var zone in info.Zones)
if (zoneSet.Type == CanvasLayoutModel.ModelTypeID)
{
zones.Add(new Int32Rect { X = (int)zone.X, Y = (int)zone.Y, Width = (int)zone.Width, Height = (int)zone.Height });
layout = ParseCanvasInfo(zoneSet);
}
else if (zoneSet.Type == GridLayoutModel.ModelTypeID)
{
layout = ParseGridInfo(zoneSet);
}
layout = new CanvasLayoutModel(zoneSet.Uuid, zoneSet.Name, LayoutType.Custom, zones, info.RefWidth, info.RefHeight);
layout.SensitivityRadius = info.SensitivityRadius;
}
else if (zoneSet.Type == GridLayoutModel.ModelTypeID)
catch (Exception)
{
var info = JsonSerializer.Deserialize<GridInfoWrapper>(zoneSet.Info.GetRawText(), _options);
var cells = new int[info.Rows, info.Columns];
for (int row = 0; row < info.Rows; row++)
{
for (int column = 0; column < info.Columns; column++)
{
cells[row, column] = info.CellChildMap[row][column];
}
}
layout = new GridLayoutModel(zoneSet.Uuid, zoneSet.Name, LayoutType.Custom, info.Rows, info.Columns, info.RowsPercentage, info.ColumnsPercentage, cells);
layout.SensitivityRadius = info.SensitivityRadius;
(layout as GridLayoutModel).ShowSpacing = info.ShowSpacing;
(layout as GridLayoutModel).Spacing = info.Spacing;
result = false;
continue;
}
else
if (layout == null)
{
result = false;
continue;
@@ -823,29 +849,105 @@ namespace FancyZonesEditor.Utils
foreach (var wrapper in templateLayouts)
{
var type = JsonTagToLayoutType(wrapper.Type);
LayoutType type = JsonTagToLayoutType(wrapper.Type);
LayoutModel layout = MainWindowSettingsModel.DefaultModels[(int)type];
foreach (var layout in MainWindowSettingsModel.DefaultModels)
layout.SensitivityRadius = wrapper.SensitivityRadius;
layout.TemplateZoneCount = wrapper.ZoneCount;
if (layout is GridLayoutModel grid)
{
if (layout.Type == type)
{
layout.SensitivityRadius = wrapper.SensitivityRadius;
layout.TemplateZoneCount = wrapper.ZoneCount;
if (layout is GridLayoutModel grid)
{
grid.ShowSpacing = wrapper.ShowSpacing;
grid.Spacing = wrapper.Spacing;
}
layout.InitTemplateZones();
}
grid.ShowSpacing = wrapper.ShowSpacing;
grid.Spacing = wrapper.Spacing;
}
layout.InitTemplateZones();
}
return true;
}
private bool SetQuickLayoutSwitchKeys(List<QuickLayoutKeysWrapper> quickSwitchKeys)
{
if (quickSwitchKeys == null)
{
return false;
}
MainWindowSettingsModel.QuickKeys.CleanUp();
foreach (var wrapper in quickSwitchKeys)
{
MainWindowSettingsModel.QuickKeys.SelectKey(wrapper.Key.ToString(), wrapper.Uuid);
}
return true;
}
private CanvasLayoutModel ParseCanvasInfo(CustomLayoutWrapper wrapper)
{
var info = JsonSerializer.Deserialize<CanvasInfoWrapper>(wrapper.Info.GetRawText(), _options);
var zones = new List<Int32Rect>();
foreach (var zone in info.Zones)
{
if (zone.Width < 0 || zone.Height < 0)
{
// Malformed data
return null;
}
zones.Add(new Int32Rect { X = zone.X, Y = zone.Y, Width = zone.Width, Height = zone.Height });
}
var layout = new CanvasLayoutModel(wrapper.Uuid, wrapper.Name, LayoutType.Custom, zones, Math.Max(info.RefWidth, 0), Math.Max(info.RefHeight, 0));
layout.SensitivityRadius = info.SensitivityRadius;
return layout;
}
private GridLayoutModel ParseGridInfo(CustomLayoutWrapper wrapper)
{
var info = JsonSerializer.Deserialize<GridInfoWrapper>(wrapper.Info.GetRawText(), _options);
// Check if rows and columns are valid
if (info.Rows <= 0 || info.Columns <= 0)
{
return null;
}
// Check if percentage is valid. Otherwise, Editor could crash on layout rendering.
foreach (int percent in info.RowsPercentage)
{
if (percent < 0)
{
return null;
}
}
foreach (int percent in info.ColumnsPercentage)
{
if (percent < 0)
{
return null;
}
}
var cells = new int[info.Rows, info.Columns];
for (int row = 0; row < info.Rows; row++)
{
for (int column = 0; column < info.Columns; column++)
{
cells[row, column] = info.CellChildMap[row][column];
}
}
var layout = new GridLayoutModel(wrapper.Uuid, wrapper.Name, LayoutType.Custom, info.Rows, info.Columns, info.RowsPercentage, info.ColumnsPercentage, cells);
layout.SensitivityRadius = info.SensitivityRadius;
layout.ShowSpacing = info.ShowSpacing;
layout.Spacing = info.Spacing;
return layout;
}
private LayoutType JsonTagToLayoutType(string tag)
{
switch (tag)

View File

@@ -48,7 +48,7 @@ namespace FancyZonesEditor.ViewModels
double maxMultiplier = MaxPreviewDisplaySize / maxDimension;
double minMultiplier = MinPreviewDisplaySize / minDimension;
DesktopPreviewMultiplier = (minMultiplier + maxMultiplier) / 3.5;
DesktopPreviewMultiplier = (minMultiplier + maxMultiplier) / 2.5;
}
private void RaisePropertyChanged(string propertyName)

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Soubor zones-settings.json obsahuje chybná data.]]></Val>
<Val><![CDATA[Rozložení, které obsahovalo neplatná data, se odebralo.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Chcete pokračovat? Chybná data se ztratí.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Klikněte a přetahujte mezi zónami.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Sloučit nebo odstranit:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Pokud chcete změnit orientaci rozdělovače, podržte klávesu Shift.]D;]A;Pokud chcete sloučit zóny, podržte levé tlačítko myši, přetáhněte myš na zónu, která se má sloučit, uvolněte tlačítko myši a klikněte na Sloučit zóny.]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Pokud chcete rychle použít rozložení, vyberte klávesu (Win + Ctrl+ Alt + klávesa).]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Žádná]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Pro svislé rozdělení podržte klávesu Shift.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Rozdělovač:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA["zones-settings.json" enthält nicht wohlgeformte Daten.]]></Val>
<Val><![CDATA[Ein Layout, das ungültige Daten enthielt, wurde entfernt.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Möchten Sie den Vorgang fortsetzen? Nicht wohlgeformte Daten gehen verloren.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Klicken und ziehen Sie Elemente von einer Zone in eine andere.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Zusammenführen/Löschen:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Halten Sie die UMSCHALTTASTE gedrückt, um die Ausrichtung des Splitters zu ändern.]D;]A;Um Zonen zusammenzuführen, halten Sie die linke Maustaste gedrückt, ziehen Sie die Maustaste auf die zusammenzuführende Zone, lassen Sie die Maustaste los, und klicken Sie auf "Zonen zusammenführen".]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Wählen Sie eine Taste für den schnellen Layoutwechsel aus (WINDOWS+STRG+ALT+Taste).]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Keine]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Halten Sie für eine vertikale Teilung die Umschalttaste gedrückt.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Teilungselement:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -451,6 +451,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Permite hacer clic y arrastrar entre zonas.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Combinar o eliminar:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +505,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Mantenga presionada la tecla Mayús para cambiar la orientación del separador.]D;]A;Para combinar zonas, mantenga presionado el botón primario del mouse, arrastre el mouse a la zona que quiera combinar, suelte el botón del mouse y haga clic en "Merge zones".]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +514,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Seleccione una clave para aplicar rápidamente el diseño (Win + Ctrl + Alt + clave).]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Ninguno]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +589,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Mantenga presionada la tecla Mayús para dividir verticalmente.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Separador:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -451,6 +451,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Cliquez et faites glisser entre les zones.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Fusionner/supprimer :]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +505,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Maintenez la touche Maj enfoncée pour changer l'orientation du séparateur.]D;]A;Pour fusionner des zones, maintenez le bouton gauche de la souris enfoncé, faites glisser la souris sur la zone à fusionner, relâchez le bouton de la souris et cliquez sur « Fusionner les zones ».]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +514,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Sélectionner une touche pour appliquer rapidement la disposition (Win + Ctrl + Alt + touche)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Aucun]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +589,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Maintenez la touche Maj enfoncée pour la division verticale.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Séparateur :]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[A zones-settings.json szabálytalan adatokat tartalmaz.]]></Val>
<Val><![CDATA[A rendszer eltávolított egy érvénytelen adatot tartalmazó elrendezést.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Folytatja? A szabálytalan adatok el fognak veszni.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Kattintson és húzza át a kurzort a zónákon.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Egyesítés/törlés:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Tartsa nyomva a Shift billentyűt a felosztó tájolásának módosításához.]D;]A;A zónák egyesítéséhez tartsa nyomva a bal egérgombot, húzza az egérmutatót az egyesíteni kívánt zónára, engedje el az egérgombot, majd kattintson a Zónák egyesítése elemre.]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Válasszon egy billentyűt az elrendezés gyors alkalmazásához (Win + Ctrl + Alt + billentyű)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Nincs]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Tartsa nyomva a Shift billentyűt a vertikális felosztáshoz.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Felosztó:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA['zones-settings.json' contiene dati non validi.]]></Val>
<Val><![CDATA[Un layout contenente dati non validi è stato rimosso.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Continuare? I dati con formato non valido andranno persi.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Fare clic e trascinare tra le zone.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Unisci/Elimina:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Tenere premuto il tasto MAIUSC per modificare l'orientamento della barra di divisione.]D;]A;Per unire le zone, tenere premuto il pulsante sinistro del mouse, trascinare il mouse sulla zona da unire, rilasciare il pulsante del mouse e fare clic su "Unisci zone".]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Selezionare un tasto per applicare rapidamente il layout (WINDOWS + CTRL + ALT + tasto)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Nessuno]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Tenere premuto il tasto MAIUSC per la divisione verticale.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Barra di divisione:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA['zones-settings.json' に誤った形式のデータが含まれています。]]></Val>
<Val><![CDATA[無効なデータを含むレイアウトが削除されました。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[続行しますか? 誤った形式のデータは失われます。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[クリックしてゾーン間でドラッグします。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[マージまたは削除:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[分割の方向を変更するには、Shift キーを押し続けます。]D;]A;ゾーンをマージするには、マウスの左ボタンを押しながらマージするゾーンにマウスをドラッグし、マウス ボタンを放して、[ゾーンのマージ]5D; をクリックします。]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[レイアウトをすばやく適用するためのキーを選択します (Win + Ctrl + Alt + キー)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[なし]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[垂直分割の場合は Shift キーを押したままにします。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[スプリッター:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA['zones-settings.json'에 잘못된 형식의 데이터가 있습니다.]]></Val>
<Val><![CDATA[잘못된 데이터가 포함된 레이아웃이 제거되었습니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[계속하시겠습니까? 잘못된 형식의 데이터가 손실됩니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[클릭하여 영역을 가로질러 끕니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[병합/삭제:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[&lt;Shift&gt; 키를 길게 눌러 분할기의 방향을 변경합니다.]D;]A;영역을 병합하려면 왼쪽 마우스 단추를 누른 상태에서 병합할 영역으로 마우스를 끌고 마우스 단추를 놓은 후 "영역 병합"을 클릭합니다.]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[레이아웃을 빠르게 적용하려면 키 선택(Win + Ctrl + Alt + 키)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[없음]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[세로 분할을 수행하려면 <Shift> 키를 길게 누릅니다.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[분할기:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[zones-settings.json' bevat gegevens met ongeldige indeling.]]></Val>
<Val><![CDATA[Een indeling die ongeldige gegevens bevat, is verwijderd.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Wilt u doorgaan? Gegevens met een ongeldige indeling gaan verloren.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Klik en sleep door zones heen.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Samenvoegen/verwijderen:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Houd SHIFT ingedrukt om de richting van de splitser te wijzigen.]D;]A;Als u zones wilt samenvoegen, houdt u de linkermuisknop ingedrukt, sleept u de muis naar de zone om samen te voegen, laat u de muisknop los en klikt u op Zones samenvoegen.]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Selecteer een toets om de indeling snel toe te passen (Win+Ctrl+Alt+toets)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Geen]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Houd de Shift-toets ingedrukt voor verticale splitsing.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Splitser:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Plik „zones-settings.json” zawiera źle sformułowane dane.]]></Val>
<Val><![CDATA[Układ, który zawierał nieprawidłowe dane, został usunięty.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Chcesz kontynuować? Uszkodzone dane zostaną utracone.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Kliknij i przeciągnij między strefami.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Scalanie/usuwanie:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Przytrzymaj wciśnięty klawisz Shift, aby zmienić orientację rozdzielacza.]D;]A;Aby scalić strefy, przytrzymaj lewy przycisk myszy, przeciągnij mysz do strefy, z którą chcesz scalić, zwolnij przycisk myszy i kliknij pozycję „Scal strefy”.]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Wybierz klawisz, aby szybko zastosować układ (Win + Ctrl + Alt + klawisz)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Brak]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Naciśnij klawisz Shift, aby podzielić pionowo.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Rozdzielacz:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -451,6 +451,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Clique no item e arraste entre as zonas.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Mesclar/Excluir:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +505,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Mantenha pressionada a tecla Shift para alterar a orientação do divisor.]D;]A;Para mesclar zonas, mantenha pressionado o botão esquerdo do mouse, arraste o mouse até a zona a ser mesclada, solte o botão do mouse e clique em "Mesclar zonas".]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +514,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Selecione uma tecla para aplicar o layout rapidamente (Win + Ctrl + Alt + tecla)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Nenhuma]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +589,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Mantenha a tecla Shift pressionada para fazer uma divisão vertical.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Divisor:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -451,6 +451,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Clique e arraste pelas zonas.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Unir/Eliminar:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +505,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Mantenha a tecla Shift premida para alterar a orientação do divisor.]D;]A;Para intercalar zonas, mantenha premido o botão esquerdo do rato, arraste o rato até à zona a intercalar, solte o botão do rato e clique em "Intercalar zonas".]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +514,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Selecione uma tecla para aplicar rapidamente o esquema (Windows + Ctrl + Alt + tecla)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Nenhum]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +589,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Mantenha premida a tecla Shift para divisão vertical.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Divisor:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA["zones-settings.json" содержит неправильно сформированные данные.]]></Val>
<Val><![CDATA[Макет, содержащий недопустимые данные, удален.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Вы хотите продолжить? Неправильно сформированные данные будут утеряны.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Щелкните и перетащите между зонами.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Объединение или удаление:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Удерживайте клавишу SHIFT, чтобы изменить ориентацию разделителя.]D;]A;Чтобы объединить зоны, удерживайте нажатой левую кнопку мыши и перетащите указатель в зону для объединения, после чего отпустите кнопку мыши и щелкните "Объединить зоны".]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Выберите клавишу для быстрого применения макета (WIN + CTRL + ALT + клавиша)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Нет]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Для разделения по вертикали удерживайте нажатой клавишу SHIFT.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Разделитель:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[zones-settings.json innehåller felaktigt utformade data.]]></Val>
<Val><![CDATA[En layout som innehöll ogiltiga data har tagits bort.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Vill du fortsätta? Felaktigt utformade data kommer att gå förlorade.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Klicka och dra mellan zoner.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Sammanslå/ta bort:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Håll ned tangenten Skift om du vill ändra orienteringen för delare.]D;]A;Om du vill sammanslå zoner håller du ned vänster musknapp och drar musen till den zon som ska sammanslås. Sedan släpper du musknappen och klickar på "Sammanslå zoner".]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Välj en tangent för att snabbt tillämpa layouten (Win + Ctrl + Alt + tangent)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Ingen]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Håll ned Skift-tangenten för vertikal delning.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Delare:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -451,6 +451,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Tıklayın ve bölgeler arasında sürükleyin.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Birleştir/Sil:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +505,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Update" Orig="New">
<Val><![CDATA[Ayırıcının yönünü değiştirmek için Shift tuşuna basılı tutun.]D;]A;Bölgeleri birleştirmek için bölgeleri seçin ve "Birleştir" seçeneğine tıklayın.]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +514,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Düzeni hızlıca uygulamak için bir tuş seçin (Win + Ctrl + Alt + tuş)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Yok]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +589,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Dikey bölme için Shift tuşunu basılı tutun.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Bölümlendirici:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA["zones-settings.json" 包含格式错误的数据。]]></Val>
<Val><![CDATA[包含了无效数据的布局已被删除。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[是否要继续操作? 格式错误的数据将丢失。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[单击并跨区域拖动。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[合并/删除:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[按住 Shift 键可以改变拆分器的方向。]D;]A;若要合并区域,请按住鼠标左键,将鼠标拖到要合并的区域,释放鼠标按钮,然后单击“合并区域”。]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[选择一个键来快速应用布局(Windows 键 + Ctrl + Alt + 你选择的键)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[无]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[按住 Shift 键进行垂直拆分。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[拆分器:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -334,11 +334,11 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_Malformed_Data" ItemType="0;.resx" PsrId="211" Leaf="true">
<Item ItemId=";Error_Parsing_Zones_Settings_Message" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['zones-settings.json' contains malformed data.]]></Val>
<Val><![CDATA[A layout that contained invalid data has been removed.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA['zones-settings.json' 包含格式錯誤的資料。]]></Val>
<Val><![CDATA[已移除包含無效資料的配置。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
@@ -352,15 +352,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Parsing_Zones_Settings_User_Choice" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Would you like to continue? Malformed data will be lost.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[您要繼續嗎? 格式錯誤的資料將會遺失。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Error_Persisting_Custom_Layout" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Error persisting custom layout]]></Val>
@@ -451,6 +442,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Click and drag across zones.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[按一下並在區域之間拖曳。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";MergeName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge/Delete:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[合併/刪除:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Merge_zones" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Merge zones]]></Val>
@@ -487,18 +496,6 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Note_Custom_Table" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, hold down the left mouse button, drag the mouse to the zone to merge, release the mouse button and click "Merge zones".]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[按住 Shift 鍵可變更分隔器的方向。]D;]A;若要合併區域,請按住滑鼠左鍵,將滑鼠拖曳到要合併的區域,放開滑鼠左鍵然後按一下 [合併區域]5D;。]]></Val>
</Tgt>
<Prev Cat="Text">
<Val><![CDATA[Hold down Shift key to change orientation of splitter.]D;]A;To merge zones, select the zones and click "merge".]]></Val>
</Prev>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";NumberOfZones" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Number of zones]]></Val>
@@ -508,6 +505,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";QuickKey_Select" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Select a key to quickly apply the layout (Win + Ctrl + Alt + key)]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[選取按鍵以快速套用版面配置 (Windows 鍵 + Ctrl + Alt + 按鍵)]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Quick_Key_None" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[None]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[無]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Reset_Layout" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Reset layout]]></Val>
@@ -565,6 +580,24 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterDescription" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Hold Shift key for vertical split.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[按住 Shift 鍵可垂直分割。]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";SplitterName" ItemType="0;.resx" PsrId="211" InstFlg="true" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[Splitter:]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[分隔器:]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";Template_Layout_Blank" ItemType="0;.resx" PsrId="211" Leaf="true">
<Str Cat="Text">
<Val><![CDATA[No layout]]></Val>

View File

@@ -0,0 +1,53 @@
#include "pch.h"
#include "CallTracer.h"
#include <thread>
namespace
{
// Non-localizable
const std::string entering = " Enter";
const std::string exiting = " Exit";
std::mutex indentLevelMutex;
std::map<std::thread::id, int> indentLevel;
std::string GetIndentation()
{
std::unique_lock lock(indentLevelMutex);
int level = indentLevel[std::this_thread::get_id()];
if (level <= 0)
{
return {};
}
else
{
return std::string(2 * min(level, 64) - 1, ' ') + " - ";
}
}
void Indent()
{
std::unique_lock lock(indentLevelMutex);
indentLevel[std::this_thread::get_id()]++;
}
void Unindent()
{
std::unique_lock lock(indentLevelMutex);
indentLevel[std::this_thread::get_id()]--;
}
}
CallTracer::CallTracer(const char* functionName) :
functionName(functionName)
{
Logger::trace((GetIndentation() + functionName + entering).c_str());
Indent();
}
CallTracer::~CallTracer()
{
Unindent();
Logger::trace((GetIndentation() + functionName + exiting).c_str());
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "common/logger/logger.h"
#define _TRACER_ CallTracer callTracer(__FUNCTION__)
class CallTracer
{
std::string functionName;
public:
CallTracer(const char* functionName);
~CallTracer();
};

View File

@@ -19,6 +19,7 @@
#include "VirtualDesktopUtils.h"
#include "MonitorWorkAreaHandler.h"
#include "util.h"
#include "CallTracer.h"
#include <lib/SecondaryMouseButtonsHook.h>
@@ -33,6 +34,30 @@ enum class DisplayChangeType
namespace
{
constexpr int CUSTOM_POSITIONING_LEFT_TOP_PADDING = 16;
struct require_read_lock
{
template<typename T>
require_read_lock(const std::shared_lock<T>& lock)
{
lock;
}
template<typename T>
require_read_lock(const std::unique_lock<T>& lock)
{
lock;
}
};
struct require_write_lock
{
template<typename T>
require_write_lock(const std::unique_lock<T>& lock)
{
lock;
}
};
}
// Non-localizable strings
@@ -89,6 +114,7 @@ public:
void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
{
_TRACER_;
std::unique_lock writeLock(m_lock);
m_windowMoveHandler.MoveSizeEnd(window, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId));
}
@@ -182,40 +208,16 @@ public:
}
LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
void OnDisplayChange(DisplayChangeType changeType) noexcept;
void AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId) noexcept;
void OnDisplayChange(DisplayChangeType changeType, require_write_lock) noexcept;
void AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId, require_write_lock) noexcept;
protected:
static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
private:
struct require_read_lock
{
template<typename T>
require_read_lock(const std::shared_lock<T>& lock)
{
lock;
}
template<typename T>
require_read_lock(const std::unique_lock<T>& lock)
{
lock;
}
};
struct require_write_lock
{
template<typename T>
require_write_lock(const std::unique_lock<T>& lock)
{
lock;
}
};
void UpdateZoneWindows() noexcept;
void UpdateWindowsPositions() noexcept;
void CycleActiveZoneSet(DWORD vkCode) noexcept;
void UpdateZoneWindows(require_write_lock) noexcept;
void UpdateWindowsPositions(require_write_lock) noexcept;
bool OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept;
bool OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept;
bool OnSnapHotkey(DWORD vkCode) noexcept;
@@ -230,12 +232,15 @@ private:
std::pair<winrt::com_ptr<IZoneWindow>, std::vector<size_t>> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, bool isPrimaryMonitor) noexcept;
void MoveWindowIntoZone(HWND window, winrt::com_ptr<IZoneWindow> zoneWindow, const std::vector<size_t>& zoneIndexSet) noexcept;
void OnEditorExitEvent() noexcept;
void UpdateZoneSets() noexcept;
void OnEditorExitEvent(require_write_lock) noexcept;
void UpdateZoneSets(require_write_lock) noexcept;
bool ShouldProcessSnapHotkey(DWORD vkCode) noexcept;
void ApplyQuickLayout(int key) noexcept;
void FlashZones(require_write_lock) noexcept;
std::vector<std::pair<HMONITOR, RECT>> GetRawMonitorData() noexcept;
std::vector<HMONITOR> GetMonitorsSorted() noexcept;
HMONITOR WorkAreaKeyFromWindow(HWND window) noexcept;
const HINSTANCE m_hinstance{};
@@ -264,6 +269,7 @@ private:
static UINT WM_PRIV_FILE_UPDATE; // Scheduled when the a watched file is updated
static UINT WM_PRIV_SNAP_HOTKEY; // Scheduled when we receive a snap hotkey key down press
static UINT WM_PRIV_QUICK_LAYOUT_KEY; // Scheduled when we receive a key down press to quickly apply a layout
// Did we terminate the editor or was it closed cleanly?
enum class EditorExitKind : byte
@@ -281,6 +287,7 @@ UINT FancyZones::WM_PRIV_VD_UPDATE = RegisterWindowMessage(L"{b8b72b46-f42f-4c26
UINT FancyZones::WM_PRIV_EDITOR = RegisterWindowMessage(L"{87543824-7080-4e91-9d9c-0404642fc7b6}");
UINT FancyZones::WM_PRIV_FILE_UPDATE = RegisterWindowMessage(L"{632f17a9-55a7-45f1-a4db-162e39271d92}");
UINT FancyZones::WM_PRIV_SNAP_HOTKEY = RegisterWindowMessage(L"{763c03a3-03d9-4cde-8d71-f0358b0b4b52}");
UINT FancyZones::WM_PRIV_QUICK_LAYOUT_KEY = RegisterWindowMessage(L"{72f4fd8e-23f1-43ab-bbbc-029363df9a84}");
// IFancyZones
IFACEMETHODIMP_(void)
@@ -421,6 +428,7 @@ std::pair<winrt::com_ptr<IZoneWindow>, std::vector<size_t>> FancyZones::GetAppZo
void FancyZones::MoveWindowIntoZone(HWND window, winrt::com_ptr<IZoneWindow> zoneWindow, const std::vector<size_t>& zoneIndexSet) noexcept
{
_TRACER_;
auto& fancyZonesData = FancyZonesDataInstance();
if (!fancyZonesData.IsAnotherWindowOfApplicationInstanceZoned(window, zoneWindow->UniqueId()))
{
@@ -550,18 +558,6 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept
bool const ctrl = GetAsyncKeyState(VK_CONTROL) & 0x8000;
if ((win && !shift && !ctrl) || (win && ctrl && alt))
{
// Temporarily disable Win+Ctrl+Number functionality
// if (ctrl)
// {
// if ((info->vkCode >= '0') && (info->vkCode <= '9'))
// {
// // Win+Ctrl+Number will cycle through ZoneSets
// Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /*inMoveSize*/);
// CycleActiveZoneSet(info->vkCode);
// return true;
// }
// }
// else
if ((info->vkCode == VK_RIGHT) || (info->vkCode == VK_LEFT) || (info->vkCode == VK_UP) || (info->vkCode == VK_DOWN))
{
if (ShouldProcessSnapHotkey(info->vkCode))
@@ -573,14 +569,34 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept
}
}
}
// Temporarily disable Win+Ctrl+Number functionality
//else if (m_inMoveSize && (info->vkCode >= '0') && (info->vkCode <= '9'))
//{
// // This allows you to cycle through ZoneSets while dragging a window
// Trace::FancyZones::OnKeyDown(info->vkCode, win, false /*control*/, true /*inMoveSize*/);
// CycleActiveZoneSet(info->vkCode);
// return false;
//}
if (m_settings->GetSettings()->quickLayoutSwitch)
{
int digitPressed = -1;
if ('0' <= info->vkCode && info->vkCode <= '9')
{
digitPressed = info->vkCode - '0';
}
else if (VK_NUMPAD0 <= info->vkCode && info->vkCode <= VK_NUMPAD9)
{
digitPressed = info->vkCode - VK_NUMPAD0;
}
bool dragging = m_windowMoveHandler.InMoveSize();
bool changeLayoutWhileNotDragging = !dragging && !shift && win && ctrl && alt && digitPressed != -1;
bool changeLayoutWhileDragging = dragging && digitPressed != -1;
if (changeLayoutWhileNotDragging || changeLayoutWhileDragging)
{
auto quickKeysMap = FancyZonesDataInstance().GetLayoutQuickKeys();
if (std::any_of(quickKeysMap.begin(), quickKeysMap.end(), [=](auto item) { return item.second == digitPressed; }))
{
PostMessageW(m_window, WM_PRIV_QUICK_LAYOUT_KEY, 0, static_cast<LPARAM>(digitPressed));
Trace::FancyZones::QuickLayoutSwitched(changeLayoutWhileNotDragging);
return true;
}
}
}
if (m_windowMoveHandler.IsDragEnabled() && shift)
{
@@ -592,6 +608,7 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept
// IFancyZonesCallback
void FancyZones::ToggleEditor() noexcept
{
_TRACER_;
{
std::shared_lock readLock(m_lock);
if (m_terminateEditorEvent)
@@ -630,7 +647,7 @@ void FancyZones::ToggleEditor() noexcept
wil::unique_cotaskmem_string virtualDesktopId;
if (!SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopId)))
{
return;
return;
}
/*
@@ -660,7 +677,7 @@ void FancyZones::ToggleEditor() noexcept
{
params += FancyZonesUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId.get()) + divider; /* Monitor id where the Editor should be opened */
}
// device id map
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
@@ -751,13 +768,16 @@ void FancyZones::ToggleEditor() noexcept
void FancyZones::SettingsChanged() noexcept
{
_TRACER_;
std::unique_lock writeLock(m_lock);
// Update the hotkey
UnregisterHotKey(m_window, 1);
RegisterHotKey(m_window, 1, m_settings->GetSettings()->editorHotkey.get_modifiers(), m_settings->GetSettings()->editorHotkey.get_code());
// Needed if we toggled spanZonesAcrossMonitors
m_workAreaHandler.Clear();
OnDisplayChange(DisplayChangeType::Initialization);
OnDisplayChange(DisplayChangeType::Initialization, writeLock);
}
// IZoneWindowHost
@@ -766,7 +786,8 @@ FancyZones::MoveWindowsOnActiveZoneSetChange() noexcept
{
if (m_settings->GetSettings()->zoneSetChange_moveWindows)
{
UpdateWindowsPositions();
std::unique_lock writeLock(m_lock);
UpdateWindowsPositions(writeLock);
}
}
@@ -789,8 +810,9 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
{
// Changes in taskbar position resulted in different size of work area.
// Invalidate cached work-areas so they can be recreated with latest information.
std::unique_lock writeLock(m_lock);
m_workAreaHandler.Clear();
OnDisplayChange(DisplayChangeType::WorkArea);
OnDisplayChange(DisplayChangeType::WorkArea, writeLock);
}
}
break;
@@ -798,8 +820,9 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
case WM_DISPLAYCHANGE:
{
// Display resolution changed. Invalidate cached work-areas so they can be recreated with latest information.
std::unique_lock writeLock(m_lock);
m_workAreaHandler.Clear();
OnDisplayChange(DisplayChangeType::DisplayChange);
OnDisplayChange(DisplayChangeType::DisplayChange, writeLock);
}
break;
@@ -814,11 +837,13 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
}
else if (message == WM_PRIV_VD_INIT)
{
OnDisplayChange(DisplayChangeType::Initialization);
std::unique_lock writeLock(m_lock);
OnDisplayChange(DisplayChangeType::Initialization, writeLock);
}
else if (message == WM_PRIV_VD_SWITCH)
{
OnDisplayChange(DisplayChangeType::VirtualDesktop);
std::unique_lock writeLock(m_lock);
OnDisplayChange(DisplayChangeType::VirtualDesktop, writeLock);
}
else if (message == WM_PRIV_VD_UPDATE)
{
@@ -832,7 +857,8 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
{
if (lparam == static_cast<LPARAM>(EditorExitKind::Exit))
{
OnEditorExitEvent();
std::unique_lock writeLock(m_lock);
OnEditorExitEvent(writeLock);
}
{
@@ -869,7 +895,12 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
else if (message == WM_PRIV_FILE_UPDATE)
{
FancyZonesDataInstance().LoadFancyZonesData();
UpdateZoneSets();
std::unique_lock writeLock(m_lock);
UpdateZoneSets(writeLock);
}
else if (message == WM_PRIV_QUICK_LAYOUT_KEY)
{
ApplyQuickLayout(static_cast<int>(lparam));
}
else
{
@@ -881,8 +912,9 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
return 0;
}
void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
void FancyZones::OnDisplayChange(DisplayChangeType changeType, require_write_lock lock) noexcept
{
_TRACER_;
if (changeType == DisplayChangeType::VirtualDesktop ||
changeType == DisplayChangeType::Initialization)
{
@@ -907,21 +939,20 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
}
}
UpdateZoneWindows();
UpdateZoneWindows(lock);
if ((changeType == DisplayChangeType::WorkArea) || (changeType == DisplayChangeType::DisplayChange))
{
if (m_settings->GetSettings()->displayChange_moveWindows)
{
UpdateWindowsPositions();
UpdateWindowsPositions(lock);
}
}
}
void FancyZones::AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId) noexcept
void FancyZones::AddZoneWindow(HMONITOR monitor, const std::wstring& deviceId, require_write_lock) noexcept
{
std::unique_lock writeLock(m_lock);
_TRACER_;
if (m_workAreaHandler.IsNewWorkArea(m_currentDesktopId, monitor))
{
wil::unique_cotaskmem_string virtualDesktopId;
@@ -968,7 +999,7 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam,
DefWindowProc(window, message, wparam, lparam);
}
void FancyZones::UpdateZoneWindows() noexcept
void FancyZones::UpdateZoneWindows(require_write_lock lock) noexcept
{
// Mapping between display device name and device index (operating system identifies each display device with an index value).
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;
@@ -976,6 +1007,7 @@ void FancyZones::UpdateZoneWindows() noexcept
{
FancyZones* fancyZones;
std::unordered_map<std::wstring, DWORD>* displayDeviceIdx;
require_write_lock lock;
};
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
@@ -987,23 +1019,23 @@ void FancyZones::UpdateZoneWindows() noexcept
FancyZones* fancyZones = params->fancyZones;
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(mi.szDevice, displayDeviceIdxMap);
fancyZones->AddZoneWindow(monitor, deviceId);
fancyZones->AddZoneWindow(monitor, deviceId, params->lock);
}
return TRUE;
};
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
{
AddZoneWindow(nullptr, {});
AddZoneWindow(nullptr, {}, lock);
}
else
{
capture capture{ this, &displayDeviceIdxMap };
capture capture{ this, &displayDeviceIdxMap, lock };
EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast<LPARAM>(&capture));
}
}
void FancyZones::UpdateWindowsPositions() noexcept
void FancyZones::UpdateWindowsPositions(require_write_lock) noexcept
{
auto callback = [](HWND window, LPARAM data) -> BOOL {
size_t bitmask = reinterpret_cast<size_t>(::GetProp(window, ZonedWindowProperties::PropertyMultipleZoneID));
@@ -1020,7 +1052,6 @@ void FancyZones::UpdateWindowsPositions() noexcept
}
auto strongThis = reinterpret_cast<FancyZones*>(data);
std::unique_lock writeLock(strongThis->m_lock);
auto zoneWindow = strongThis->m_workAreaHandler.GetWorkArea(window);
if (zoneWindow)
{
@@ -1032,37 +1063,10 @@ void FancyZones::UpdateWindowsPositions() noexcept
EnumWindows(callback, reinterpret_cast<LPARAM>(this));
}
void FancyZones::CycleActiveZoneSet(DWORD vkCode) noexcept
{
auto window = GetForegroundWindow();
if (FancyZonesUtils::IsCandidateForZoning(window, m_settings->GetSettings()->excludedAppsArray))
{
const HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
if (monitor)
{
std::shared_lock readLock(m_lock);
auto zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor);
if (zoneWindow)
{
zoneWindow->CycleActiveZoneSet(vkCode);
}
}
}
}
bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept
{
HMONITOR current;
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
{
current = NULL;
}
else
{
current = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
}
_TRACER_;
HMONITOR current = WorkAreaKeyFromWindow(window);
std::vector<HMONITOR> monitorInfo = GetMonitorsSorted();
if (current && monitorInfo.size() > 1 && m_settings->GetSettings()->moveWindowAcrossMonitors)
@@ -1120,16 +1124,7 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce
bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
{
HMONITOR current;
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
{
current = NULL;
}
else
{
current = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
}
HMONITOR current = WorkAreaKeyFromWindow(window);
auto allMonitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
@@ -1283,6 +1278,7 @@ bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle
void FancyZones::RegisterVirtualDesktopUpdates(std::vector<GUID>& ids) noexcept
{
_TRACER_;
std::unique_lock writeLock(m_lock);
m_workAreaHandler.RegisterUpdates(ids);
@@ -1305,14 +1301,14 @@ bool FancyZones::IsSplashScreen(HWND window)
return wcscmp(NonLocalizable::SplashClassName, className) == 0;
}
void FancyZones::OnEditorExitEvent() noexcept
void FancyZones::OnEditorExitEvent(require_write_lock lock) noexcept
{
// Collect information about changes in zone layout after editor exited.
FancyZonesDataInstance().LoadFancyZonesData();
UpdateZoneSets();
UpdateZoneSets(lock);
}
void FancyZones::UpdateZoneSets() noexcept
void FancyZones::UpdateZoneSets(require_write_lock lock) noexcept
{
for (auto workArea : m_workAreaHandler.GetAllWorkAreas())
{
@@ -1320,7 +1316,7 @@ void FancyZones::UpdateZoneSets() noexcept
}
if (m_settings->GetSettings()->zoneSetChange_moveWindows)
{
UpdateWindowsPositions();
UpdateWindowsPositions(lock);
}
}
@@ -1329,15 +1325,7 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept
auto window = GetForegroundWindow();
if (m_settings->GetSettings()->overrideSnapHotkeys && FancyZonesUtils::IsCandidateForZoning(window, m_settings->GetSettings()->excludedAppsArray))
{
HMONITOR monitor;
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
{
monitor = NULL;
}
else
{
monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
}
HMONITOR monitor = WorkAreaKeyFromWindow(window);
auto zoneWindow = m_workAreaHandler.GetWorkArea(m_currentDesktopId, monitor);
if (zoneWindow && zoneWindow->ActiveZoneSet() && zoneWindow->ActiveZoneSet()->LayoutType() != FancyZonesDataTypes::ZoneSetLayoutType::Blank)
@@ -1355,6 +1343,47 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept
return false;
}
void FancyZones::ApplyQuickLayout(int key) noexcept
{
std::unique_lock writeLock(m_lock);
std::wstring uuid;
for (auto [zoneUuid, hotkey] : FancyZonesDataInstance().GetLayoutQuickKeys())
{
if (hotkey == key)
{
uuid = zoneUuid;
}
}
auto workArea = m_workAreaHandler.GetWorkAreaFromCursor(m_currentDesktopId);
// Find a custom zone set with this uuid and apply it
auto customZoneSets = FancyZonesDataInstance().GetCustomZoneSetsMap();
if (!customZoneSets.contains(uuid))
{
return;
}
FancyZonesDataTypes::ZoneSetData data{ .uuid = uuid, .type = FancyZonesDataTypes::ZoneSetLayoutType::Custom };
FancyZonesDataInstance().SetActiveZoneSet(workArea->UniqueId(), data);
FancyZonesDataInstance().SaveZoneSettings();
UpdateZoneSets(writeLock);
FlashZones(writeLock);
}
void FancyZones::FlashZones(require_write_lock) noexcept
{
if (m_settings->GetSettings()->flashZonesOnQuickSwitch && !m_windowMoveHandler.IsDragEnabled())
{
for (auto [monitor, workArea] : m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId))
{
workArea->FlashZones();
}
}
}
std::vector<HMONITOR> FancyZones::GetMonitorsSorted() noexcept
{
std::shared_lock readLock(m_lock);
@@ -1368,6 +1397,7 @@ std::vector<HMONITOR> FancyZones::GetMonitorsSorted() noexcept
std::vector<std::pair<HMONITOR, RECT>> FancyZones::GetRawMonitorData() noexcept
{
_TRACER_;
std::shared_lock readLock(m_lock);
std::vector<std::pair<HMONITOR, RECT>> monitorInfo;
@@ -1385,6 +1415,18 @@ std::vector<std::pair<HMONITOR, RECT>> FancyZones::GetRawMonitorData() noexcept
return monitorInfo;
}
HMONITOR FancyZones::WorkAreaKeyFromWindow(HWND window) noexcept
{
if (m_settings->GetSettings()->spanZonesAcrossMonitors)
{
return NULL;
}
else
{
return MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
}
}
winrt::com_ptr<IFancyZones> MakeFancyZones(HINSTANCE hinstance,
const winrt::com_ptr<IFancyZonesSettings>& settings,
std::function<void()> disableCallback) noexcept

View File

@@ -4,6 +4,7 @@
#include "JsonHelpers.h"
#include "ZoneSet.h"
#include "Settings.h"
#include "CallTracer.h"
#include <common/utils/json.h>
#include <fancyzones/lib/util.h>
@@ -153,6 +154,24 @@ FancyZonesData::FancyZonesData()
editorParametersFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesEditorParametersFile);
}
const JSONHelpers::TDeviceInfoMap& FancyZonesData::GetDeviceInfoMap() const
{
std::scoped_lock lock{ dataLock };
return deviceInfoMap;
}
const JSONHelpers::TCustomZoneSetsMap& FancyZonesData::GetCustomZoneSetsMap() const
{
std::scoped_lock lock{ dataLock };
return customZoneSetsMap;
}
const std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>& FancyZonesData::GetAppZoneHistoryMap() const
{
std::scoped_lock lock{ dataLock };
return appZoneHistoryMap;
}
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const std::wstring& zoneWindowId) const
{
std::scoped_lock lock{ dataLock };
@@ -169,6 +188,7 @@ std::optional<FancyZonesDataTypes::CustomZoneSetData> FancyZonesData::FindCustom
bool FancyZonesData::AddDevice(const std::wstring& deviceId)
{
_TRACER_;
using namespace FancyZonesDataTypes;
std::scoped_lock lock{ dataLock };
@@ -214,6 +234,7 @@ void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstr
void FancyZonesData::UpdatePrimaryDesktopData(const std::wstring& desktopId)
{
_TRACER_;
// Explorer persists current virtual desktop identifier to registry on a per session basis,
// but only after first virtual desktop switch happens. If the user hasn't switched virtual
// desktops in this session value in registry will be empty and we will use default GUID in
@@ -377,6 +398,7 @@ std::vector<size_t> FancyZonesData::GetAppLastZoneIndexSet(HWND window, const st
bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId)
{
_TRACER_;
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
if (!processPath.empty())
@@ -429,6 +451,7 @@ bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& dev
bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const std::vector<size_t>& zoneIndexSet)
{
_TRACER_;
std::scoped_lock lock{ dataLock };
if (IsAnotherWindowOfApplicationInstanceZoned(window, deviceId))
@@ -488,10 +511,33 @@ bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId,
void FancyZonesData::SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& data)
{
std::scoped_lock lock{ dataLock };
auto it = deviceInfoMap.find(deviceId);
if (it != deviceInfoMap.end())
auto deviceIt = deviceInfoMap.find(deviceId);
if (deviceIt == deviceInfoMap.end())
{
it->second.activeZoneSet = data;
return;
}
deviceIt->second.activeZoneSet = data;
// If the zone set is custom, we need to copy its properties to the device
auto zonesetIt = customZoneSetsMap.find(data.uuid);
if (zonesetIt != customZoneSetsMap.end())
{
if (zonesetIt->second.type == FancyZonesDataTypes::CustomLayoutType::Grid)
{
auto layoutInfo = std::get<FancyZonesDataTypes::GridLayoutInfo>(zonesetIt->second.info);
deviceIt->second.sensitivityRadius = layoutInfo.sensitivityRadius();
deviceIt->second.showSpacing = layoutInfo.showSpacing();
deviceIt->second.spacing = layoutInfo.spacing();
deviceIt->second.zoneCount = layoutInfo.zoneCount();
}
else if (zonesetIt->second.type == FancyZonesDataTypes::CustomLayoutType::Canvas)
{
auto layoutInfo = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(zonesetIt->second.info);
deviceIt->second.sensitivityRadius = layoutInfo.sensitivityRadius;
deviceIt->second.zoneCount = (int)layoutInfo.zones.size();
}
}
}
@@ -513,6 +559,7 @@ void FancyZonesData::LoadFancyZonesData()
appZoneHistoryMap = JSONHelpers::ParseAppZoneHistory(fancyZonesDataJSON);
deviceInfoMap = JSONHelpers::ParseDeviceInfos(fancyZonesDataJSON);
customZoneSetsMap = JSONHelpers::ParseCustomZoneSets(fancyZonesDataJSON);
quickKeysMap = JSONHelpers::ParseQuickKeys(fancyZonesDataJSON);
}
}
@@ -524,14 +571,14 @@ void FancyZonesData::SaveAppZoneHistoryAndZoneSettings() const
void FancyZonesData::SaveZoneSettings() const
{
Logger::trace("FancyZonesData::SaveZoneSettings()");
_TRACER_;
std::scoped_lock lock{ dataLock };
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, deviceInfoMap, customZoneSetsMap);
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, deviceInfoMap, customZoneSetsMap, quickKeysMap);
}
void FancyZonesData::SaveAppZoneHistory() const
{
Logger::trace("FancyZonesData::SaveAppZoneHistory()");
_TRACER_;
std::scoped_lock lock{ dataLock };
JSONHelpers::SaveAppZoneHistory(appZoneHistoryFileName, appZoneHistoryMap);
}

View File

@@ -42,22 +42,16 @@ public:
std::optional<FancyZonesDataTypes::CustomZoneSetData> FindCustomZoneSet(const std::wstring& guid) const;
inline const JSONHelpers::TDeviceInfoMap & GetDeviceInfoMap() const
{
std::scoped_lock lock{ dataLock };
return deviceInfoMap;
}
const JSONHelpers::TDeviceInfoMap& GetDeviceInfoMap() const;
inline const JSONHelpers::TCustomZoneSetsMap & GetCustomZoneSetsMap() const
{
std::scoped_lock lock{ dataLock };
return customZoneSetsMap;
}
const JSONHelpers::TCustomZoneSetsMap& GetCustomZoneSetsMap() const;
inline const std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>& GetAppZoneHistoryMap() const
const std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>& GetAppZoneHistoryMap() const;
inline const JSONHelpers::TLayoutQuickKeysMap& GetLayoutQuickKeys() const
{
std::scoped_lock lock{ dataLock };
return appZoneHistoryMap;
return quickKeysMap;
}
inline const std::wstring& GetZonesSettingsFileName() const
@@ -133,6 +127,8 @@ private:
JSONHelpers::TDeviceInfoMap deviceInfoMap{};
// Maps custom zoneset UUID to it's data
JSONHelpers::TCustomZoneSetsMap customZoneSetsMap{};
// Maps zoneset UUID with quick access keys
JSONHelpers::TLayoutQuickKeysMap quickKeysMap{};
std::wstring zonesSettingsFileName;
std::wstring appZoneHistoryFileName;

View File

@@ -112,4 +112,18 @@ namespace FancyZonesDataTypes
cellRow.resize(m_columns, 0);
}
}
int GridLayoutInfo::zoneCount() const
{
int high = 0;
for (const auto& row : m_cellChildMap)
{
for (int val : row)
{
high = max(high, val);
}
}
return high + 1;
}
}

Some files were not shown because too many files have changed in this diff Show More