diff --git a/.github/actions/spell-check/expect/expect.txt b/.github/actions/spell-check/expect/expect.txt index 01b64c42d78..276d9778fae 100644 --- a/.github/actions/spell-check/expect/expect.txt +++ b/.github/actions/spell-check/expect/expect.txt @@ -1457,6 +1457,7 @@ natvis naws nbsp Nc +NCACTIVATE NCCALCSIZE NCCREATE NCLBUTTONDOWN diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp index ee5f3e0d9b7..cf145bbe917 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.cpp +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.cpp @@ -79,6 +79,18 @@ namespace winrt::TerminalApp::implementation CloseButton().Height(maximizedHeight); break; + case WindowVisualState::WindowVisualStateUnfocused: + VisualStateManager::GoToState(MaximizeButton(), L"WindowStateUnfocused", false); + VisualStateManager::GoToState(MinimizeButton(), L"WindowStateUnfocused", false); + VisualStateManager::GoToState(CloseButton(), L"WindowStateUnfocused", false); + break; + + case WindowVisualState::WindowVisualStateFocused: + VisualStateManager::GoToState(MaximizeButton(), L"WindowStateFocused", false); + VisualStateManager::GoToState(MinimizeButton(), L"WindowStateFocused", false); + VisualStateManager::GoToState(CloseButton(), L"WindowStateFocused", false); + break; + case WindowVisualState::WindowVisualStateNormal: case WindowVisualState::WindowVisualStateIconified: default: diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.xaml b/src/cascadia/TerminalApp/MinMaxCloseControl.xaml index 701c8720935..edd21725ceb 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.xaml +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.xaml @@ -22,6 +22,7 @@ the MIT License. See LICENSE in the project root for license information. --> + @@ -35,6 +36,7 @@ the MIT License. See LICENSE in the project root for license information. --> + @@ -49,6 +51,7 @@ the MIT License. See LICENSE in the project root for license information. --> + @@ -125,6 +128,16 @@ the MIT License. See LICENSE in the project root for license information. --> + + + + + + + + + + diff --git a/src/cascadia/TerminalApp/TitlebarControl.cpp b/src/cascadia/TerminalApp/TitlebarControl.cpp index 05806726cfe..4c31950669a 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.cpp +++ b/src/cascadia/TerminalApp/TitlebarControl.cpp @@ -66,6 +66,13 @@ namespace winrt::TerminalApp::implementation } } + void TitlebarControl::Focused(bool focused) + { + const auto state = focused ? winrt::TerminalApp::WindowVisualState::WindowVisualStateFocused : + winrt::TerminalApp::WindowVisualState::WindowVisualStateUnfocused; + MinMaxCloseControl().SetWindowVisualState(state); + } + void TitlebarControl::Maximize_Click(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/) { _OnMaximizeOrRestore(HTMAXBUTTON); diff --git a/src/cascadia/TerminalApp/TitlebarControl.h b/src/cascadia/TerminalApp/TitlebarControl.h index a30f347d868..65778d70910 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.h +++ b/src/cascadia/TerminalApp/TitlebarControl.h @@ -17,6 +17,9 @@ namespace winrt::TerminalApp::implementation { TitlebarControl(uint64_t handle); + bool Focused() const; + void Focused(bool focused); + IInspectable Content(); void Content(IInspectable content); diff --git a/src/cascadia/TerminalApp/TitlebarControl.idl b/src/cascadia/TerminalApp/TitlebarControl.idl index 104f7a2f26f..0b2d43dcf98 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.idl +++ b/src/cascadia/TerminalApp/TitlebarControl.idl @@ -7,7 +7,9 @@ namespace TerminalApp { WindowVisualStateNormal = 0, WindowVisualStateMaximized, - WindowVisualStateIconified + WindowVisualStateIconified, + WindowVisualStateFocused, + WindowVisualStateUnfocused }; [default_interface] runtimeclass TitlebarControl : Windows.UI.Xaml.Controls.Grid @@ -15,6 +17,8 @@ namespace TerminalApp TitlebarControl(UInt64 parentWindowHandle); void SetWindowVisualState(WindowVisualState visualState); + Boolean Focused { get; set; }; + IInspectable Content; Windows.UI.Xaml.Controls.Border DragBar { get; }; } diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index d8e67e7dec7..595a16ae28c 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -516,6 +516,32 @@ int NonClientIslandWindow::_GetResizeHandleHeight() const noexcept return 0; } +// Method Description: +// - Responds to the WM_NCACTIVATE message by changing the focus state of +// the window. +[[nodiscard]] LRESULT NonClientIslandWindow::_OnFocusChanged(const WPARAM wParam, const LPARAM lParam) noexcept +{ + const auto windowStyle = GetWindowStyle(_window.get()); + const auto isIconified = WI_IsFlagSet(windowStyle, WS_ICONIC); + + if (isIconified) + { + return DefWindowProc(_window.get(), WM_NCACTIVATE, wParam, lParam); + } + + if (_titlebar) + { + _isFocused = wParam; + try + { + _titlebar.Focused(_isFocused); + } + CATCH_LOG(); + } + + return TRUE; +} + // Method Description: // - Hit test the frame for resizing and moving. // Arguments: @@ -689,6 +715,8 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept return 0; case WM_NCCALCSIZE: return _OnNcCalcSize(wParam, lParam); + case WM_NCACTIVATE: + return _OnFocusChanged(wParam, lParam); case WM_NCHITTEST: return _OnNcHitTest({ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }); case WM_PAINT: diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h index 10a39b4fcb1..b510611f106 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h @@ -61,6 +61,7 @@ class NonClientIslandWindow : public IslandWindow winrt::Windows::UI::Xaml::ElementTheme _theme; bool _isMaximized; + bool _isFocused; [[nodiscard]] static LRESULT __stdcall _StaticInputSinkWndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; [[nodiscard]] LRESULT _InputSinkMessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; @@ -73,6 +74,7 @@ class NonClientIslandWindow : public IslandWindow [[nodiscard]] LRESULT _OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept override; [[nodiscard]] LRESULT _OnNcCalcSize(const WPARAM wParam, const LPARAM lParam) noexcept; + [[nodiscard]] LRESULT _OnFocusChanged(const WPARAM wParam, const LPARAM lParam) noexcept; [[nodiscard]] LRESULT _OnNcHitTest(POINT ptMouse) const noexcept; [[nodiscard]] LRESULT _OnPaint() noexcept; [[nodiscard]] LRESULT _OnSetCursor(WPARAM wParam, LPARAM lParam) const noexcept;