Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable setting an initial position and maximization launch for Terminal #2817

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9e85571
Spec version 1 for set terminal initial position
KaiyuWang16 Sep 3, 2019
dbbba4e
Fix the spec title
KaiyuWang16 Sep 3, 2019
5a919c7
Add a new function to read initial position properties
KaiyuWang16 Sep 4, 2019
5e8dc81
Merge branch 'master' into dev/kawa/1043-set-initial-position-for-the…
KaiyuWang16 Sep 4, 2019
5e947ac
Test version - 9-6
KaiyuWang16 Sep 6, 2019
8fbe307
spec for code review updates
KaiyuWang16 Sep 6, 2019
f5926f8
Code change 9-11
KaiyuWang16 Sep 12, 2019
6c59f37
temporary changes 9 12
KaiyuWang16 Sep 12, 2019
e2999b1
Enable setting an initial position for terminal and maximization launch
KaiyuWang16 Sep 19, 2019
6206ecb
remove empty lines
KaiyuWang16 Sep 19, 2019
524f589
remove empty lines
KaiyuWang16 Sep 19, 2019
bea181f
Support resize when the Terminal Window hangs off the screen
KaiyuWang16 Sep 23, 2019
c60b074
Merge branch 'master' into dev/kawa/1043-set-initial-position-for-the…
KaiyuWang16 Sep 24, 2019
621c2c9
sync to master, fix conflict and typing warnings
KaiyuWang16 Sep 24, 2019
e285ac4
Remove unreferenced parameter to fix warnings
KaiyuWang16 Sep 24, 2019
9fc29b6
fix format issues
KaiyuWang16 Sep 24, 2019
9520adb
remove support for launch minimization
KaiyuWang16 Sep 24, 2019
1a24936
Code review changes
KaiyuWang16 Sep 27, 2019
9433b72
remove spec in this PR
KaiyuWang16 Sep 27, 2019
a682e6d
Move launchMode0 string serializarion to GlobalAppSettings
KaiyuWang16 Sep 28, 2019
1a330d7
remove useless default value definition and fix typos
KaiyuWang16 Sep 30, 2019
3ba5eb8
Merge branch 'master' into dev/kawa/1043-set-initial-position-for-the…
KaiyuWang16 Sep 30, 2019
a7cc388
Use std::optional<> for initial X/Y value, fix format issue
KaiyuWang16 Oct 10, 2019
c700753
Combine the initial X/Y position into one string property
KaiyuWang16 Oct 11, 2019
bba2b9b
Change the return type of AppHost::_HandleCreateWindow back to void
KaiyuWang16 Oct 11, 2019
929e8ae
Elinamate useless bools for initialX/Y
KaiyuWang16 Oct 12, 2019
1cf8f83
remove useless setters in GlobalAppSettings
KaiyuWang16 Oct 14, 2019
e39b110
Remove useless variables in GlobalAppSettings
KaiyuWang16 Oct 15, 2019
5cf1037
Merge remote-tracking branch 'github/master' into HEAD
DHowett Oct 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
102 changes: 102 additions & 0 deletions doc/specs/#1043 - Set the initial position of the Terminal/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
author: Kaiyu Wang KaiyuWang16/kawa@microsoft.com
created on: 2019-09-03
last updated: 2019-09-03
issue id: #1043
---

# Set the initial position for terminal

## Abstract

This spec is for task #1043 �Be able to set an initial position for the terminal�. It goes over the details of a new feature that allows users to set the initial position and size of the terminal. Expected behavior and design of this feature is included. Besides, future possible follow-up works are also addressed.

## Inspiration

The idea is to allow users to set the initial position of the Terminal when they launch it, prevent the Terminal from appearing on unexpected position (e.g. outside of the screen bounds). We are also going to let users choose to maximize the window when they launch it.

## Solution Design

For now, the Terminal window is put on a default initial position. The program uses CW_USEDEFAULT in the screen coordinates for top-left corner. We have two different types of window � client window and non-client window. However, code path for window creation (WM_CREATE message is shared by the two types of windows) are almost the same for the two types of windows, except that there are some differences in calculation of the width and height of the window.

Four new properties should be added in the json settings file:

**initialX**: int. This set the initial horizontal position of the top-left corner of the window. This property is optional. If not provided, system default position will be used.

**initialY**: int. This set the initial vertical position of the top-left corner of the window. This property is optional. If not provided, system default position will be used.

**launchMode**: string. Determine the launch mode. There are two modes for now
1. maximize: the window will be maximized when launch.
2. default: the window will be initialized with system default size.

The steps of this process:

1. Set the top-left origin, width and height to CW_USEDEFAULT.
2. Get the dpi of the nearest monitor; Load settings.
3. From settings, find the user-defined initial position and launch mode.
4. If the user sets custom initial position, calculate the new position considering the current dpi and monitor. If not, use system default value.
5. If the user set launch mode as "maximize", calculate the new height and width. If the user choose "default", use system default size.
6. SetWindowPos with the new position and dimension of the window.

Step 2 to 6 should be done in `AppHost::_HandleCreateWindow`, which is consistent to the current code.

In step 4, we may need to consider the dpi of the current monitor and multi-monitor scenario when calculating the initial position of the window.

Edge cases:

1. Multiple monitors. The user should be able to set the initial position to any monitors attached. For the monitors on the left side of the major monitor, the initial position values are negative.
2. If the initial position is larger than the screen resolution and the window is off-screen, we should at least let user be able to see and drag the window back on screen. One solution is to set the initial position to the top left corner of the nearest monitor if the titlebar is off-screen.
3. If the user wants to launch maximized and provides an initial position, we should launch the maximized window on the monitor where the position is located.

## UI/UX Design

Upon successful implementation, the user is able to add new properties to the json profile file, which is illustrated in the picture below:

![Sol Design](images/ProfileSnapshot.png)

The rest of the UI will be the same of the current Terminal experience, except that the initial position may be different.

## Capabilities

### Accessibility

This feature will not impact accessibility of Windows Terminal.

### Security

This should not introduce any new security issues.

### Reliability

This new feature allows the users to set custom initial position of the Terminal App window, which helps them to avoid embarassing window launch situation such as launching outside the screen bounds. Thus, it improves the reliability.

### Compatibility

This feature won't break existing features of Terminal.

### Performance, Power, and Efficiency

More data reading and calculation will be included in Terminal Launch process, which may inversely influence the launch time. However, the impact is trivial.

## Potential Issues

1. The dpi of the monitor may impact the initial position even if the json profile settings are the same. We need to consider the dpi of the user's monitor and calculate the initial position in the current screen coordinates. Testing with different monitor dpi are also necessary.

2. We need to consider multi-monitor scenario. If the user has multiple monitors, we must guarantee that the Terminal could be iniitalized as expected. More discussions on what should we do in this scenario with the github community is needed.

## Future considerations

For now, this feature only allows the user to set initial positon and choose whether to maximize the window when launch. In the future, we may consider follow-up features like:

1. Save the position of the Terminal on exit, and restore the position on the next launch. This could be a true/false feature that users could choose to set.

2. We may need to consider multiple Terminal windows scenario. If the user opens multiple Terminal windows, then we need to consider how to save and restore the position.

3. We may also consider more launch modes. Like full screen mode and minimized mode.

Github issue for future follow-ups: https://github.com/microsoft/terminal/issues/766

## Resources

Github issue:
https://github.com/microsoft/terminal/issues/1043
52 changes: 52 additions & 0 deletions src/cascadia/TerminalApp/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,58 @@ namespace winrt::TerminalApp::implementation
return TermControl::GetProposedDimensions(settings, dpi);
}

// Method Description:
// - Get the launch mode in json settings file. Now there
// two launch mode: default, maximize. Default means the window
// will launch according to the launch dimensions provided. Maximize
// means the window will launch as a maximized window
// Arguments:
// - <none>
// Return Value:
// - a string representing launch mode: "default" or "maximize"
hstring App::GetLaunchMode()
{
if (!_loadedInitialSettings)
{
// Load settings if we haven't already
LoadSettings();
}

return _settings->GlobalSettings().GetLaunchMode();
}

// Method Description:
// - Get the user defined initial position from Json settings file.
// This position represents the top left corner of the Terminal window.
// This setting is optional, if not provided, we will use the system
// default size, which is provided in IslandWindow::MakeWindow.
// Arguments:
// - defaultInitialX: the system default x coordinate value
// - defaultInitialY: the system defualt y coordinate value
// Return Value:
// - a point containing the requested initial position in pixels.
winrt::Windows::Foundation::Point App::GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY)
{
if (!_loadedInitialSettings)
{
// Load settings if we haven't already
LoadSettings();
}

winrt::Windows::Foundation::Point point(defaultInitialX, defaultInitialY);

if (!_settings->GlobalSettings().GetUseDefaultInitialX())
{
point.X = _settings->GlobalSettings().GetInitialX();
}
if (!_settings->GlobalSettings().GetUseDefaultInitialY())
{
point.Y = _settings->GlobalSettings().GetInitialY();
}

return point;
}

bool App::GetShowTabsInTitlebar()
{
if (!_loadedInitialSettings)
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ namespace winrt::TerminalApp::implementation
void LoadSettings();

Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi);
winrt::Windows::Foundation::Point GetLaunchInitialPositions(const uint64_t defaultInitialX, const uint64_t defaultInitialY);
hstring GetLaunchMode();
bool GetShowTabsInTitlebar();

Windows::UI::Xaml::UIElement GetRoot() noexcept;
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/App.idl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ namespace TerminalApp
String Title { get; };

Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi);
Windows.Foundation.Point GetLaunchInitialPositions(UInt64 defaultInitialX, UInt64 defaultInitialY);
String GetLaunchMode();
Boolean GetShowTabsInTitlebar();
void TitlebarClicked();

Expand Down
67 changes: 64 additions & 3 deletions src/cascadia/TerminalApp/GlobalAppSettings.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include <WinUser.h>
#include "pch.h"
#include "GlobalAppSettings.h"
#include "../../types/inc/Utils.hpp"
Expand All @@ -20,12 +21,14 @@ static constexpr std::string_view DefaultProfileKey{ "defaultProfile" };
static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" };
static constexpr std::string_view InitialRowsKey{ "initialRows" };
static constexpr std::string_view InitialColsKey{ "initialCols" };
static constexpr std::string_view InitialXKey{ "initialX" };
static constexpr std::string_view InitialYKey{ "initialY" };
static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" };
static constexpr std::string_view RequestedThemeKey{ "requestedTheme" };
static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" };
static constexpr std::string_view WordDelimitersKey{ "wordDelimiters" };
static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" };

static constexpr std::string_view LaunchModeKey{ "launchMode" };
static constexpr std::wstring_view LightThemeValue{ L"light" };
static constexpr std::wstring_view DarkThemeValue{ L"dark" };
static constexpr std::wstring_view SystemThemeValue{ L"system" };
Expand All @@ -37,11 +40,16 @@ GlobalAppSettings::GlobalAppSettings() :
_alwaysShowTabs{ true },
_initialRows{ DEFAULT_ROWS },
_initialCols{ DEFAULT_COLS },
_initialX{ 0 },
_initialY{ 0 },
_useDefaultInitialX{ true },
_useDefaultInitialY{ true },
_showTitleInTitlebar{ true },
_showTabsInTitlebar{ true },
_requestedTheme{ ElementTheme::Default },
_wordDelimiters{ DEFAULT_WORD_DELIMITERS },
_copyOnSelect{ false }
_copyOnSelect{ false },
_launchMode{ DEFAULT_LAUNCH_MODE }
{
}

Expand Down Expand Up @@ -129,6 +137,15 @@ void GlobalAppSettings::SetCopyOnSelect(const bool copyOnSelect) noexcept
_copyOnSelect = copyOnSelect;
}

winrt::hstring GlobalAppSettings::GetLaunchMode() const noexcept
{
return _launchMode;
}
void GlobalAppSettings::SetLaunchMode(const std::wstring launchMode)
{
_launchMode = launchMode;
}

#pragma region ExperimentalSettings
bool GlobalAppSettings::GetShowTabsInTitlebar() const noexcept
{
Expand All @@ -139,6 +156,32 @@ void GlobalAppSettings::SetShowTabsInTitlebar(const bool showTabsInTitlebar) noe
{
_showTabsInTitlebar = showTabsInTitlebar;
}

int32_t GlobalAppSettings::GetInitialX() const noexcept
{
return _initialX;
}
void GlobalAppSettings::SetInitialX(const int32_t initialX) noexcept
{
_initialX = initialX;
}
bool GlobalAppSettings::GetUseDefaultInitialX() const noexcept
{
return _useDefaultInitialX;
}
int32_t GlobalAppSettings::GetInitialY() const noexcept
{
return _initialY;
}
void GlobalAppSettings::SetInitialY(const int32_t initialY) noexcept
{
_initialY = initialY;
}
bool GlobalAppSettings::GetUseDefaultInitialY() const noexcept
{
return _useDefaultInitialY;
}

#pragma endregion

// Method Description:
Expand All @@ -152,6 +195,7 @@ void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexce
settings.KeyBindings(GetKeybindings());
settings.InitialRows(_initialRows);
settings.InitialCols(_initialCols);

settings.WordDelimiters(_wordDelimiters);
settings.CopyOnSelect(_copyOnSelect);
}
Expand All @@ -169,11 +213,14 @@ Json::Value GlobalAppSettings::ToJson() const
jsonObject[JsonKey(DefaultProfileKey)] = winrt::to_string(Utils::GuidToString(_defaultProfile));
jsonObject[JsonKey(InitialRowsKey)] = _initialRows;
jsonObject[JsonKey(InitialColsKey)] = _initialCols;
jsonObject[JsonKey(InitialXKey)] = _initialX;
jsonObject[JsonKey(InitialYKey)] = _initialY;
jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs;
jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar;
jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar;
jsonObject[JsonKey(WordDelimitersKey)] = winrt::to_string(_wordDelimiters);
jsonObject[JsonKey(CopyOnSelectKey)] = _copyOnSelect;
jsonObject[JsonKey(LaunchModeKey)] = winrt::to_string(_launchMode);
jsonObject[JsonKey(RequestedThemeKey)] = winrt::to_string(_SerializeTheme(_requestedTheme));
jsonObject[JsonKey(KeybindingsKey)] = AppKeyBindingsSerialization::ToJson(_keybindings);

Expand Down Expand Up @@ -208,7 +255,16 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json)
{
result._initialCols = initialCols.asInt();
}

if (auto initialX{ json[JsonKey(InitialXKey)] })
{
result._useDefaultInitialX = false;
result._initialX = initialX.asInt();
}
if (auto initialY{ json[JsonKey(InitialYKey)] })
{
result._useDefaultInitialY = false;
result._initialY = initialY.asInt();
}
if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] })
{
result._showTitleInTitlebar = showTitleInTitlebar.asBool();
Expand All @@ -229,6 +285,11 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json)
result._copyOnSelect = copyOnSelect.asBool();
}

if (auto launchMode{ json[JsonKey(LaunchModeKey)] })
{
result._launchMode = GetWstringFromJson(launchMode);
}

if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] })
{
result._requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme));
Expand Down
18 changes: 18 additions & 0 deletions src/cascadia/TerminalApp/GlobalAppSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ class TerminalApp::GlobalAppSettings final
bool GetCopyOnSelect() const noexcept;
void SetCopyOnSelect(const bool copyOnSelect) noexcept;

int32_t GetInitialX() const noexcept;
void SetInitialX(const int32_t initialX) noexcept;
bool GetUseDefaultInitialX() const noexcept;

int32_t GetInitialY() const noexcept;
void SetInitialY(const int32_t initialY) noexcept;
bool GetUseDefaultInitialY() const noexcept;

winrt::hstring GetLaunchMode() const noexcept;
void SetLaunchMode(const std::wstring launchMode);

winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept;

Json::Value ToJson() const;
Expand All @@ -69,6 +80,11 @@ class TerminalApp::GlobalAppSettings final
int32_t _initialRows;
int32_t _initialCols;

int _initialX;
int32_t _initialY;
bool _useDefaultInitialX;
bool _useDefaultInitialY;

bool _showStatusline;
bool _alwaysShowTabs;
bool _showTitleInTitlebar;
Expand All @@ -78,6 +94,8 @@ class TerminalApp::GlobalAppSettings final
bool _copyOnSelect;
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;

winrt::hstring _launchMode;

static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept;
static std::wstring_view _SerializeTheme(const winrt::Windows::UI::Xaml::ElementTheme theme) noexcept;
};
Loading