Skip to content

Commit 796d171

Browse files
greedyAIjoseartrivera
authored andcommitted
Always-on-Top mode implemented (#579)
1 parent af83226 commit 796d171

30 files changed

+847
-123
lines changed

docs/ManualTests.md

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,59 @@ Steps:
5454
2. Select “miles” as the unit type in the output field
5555
*Expected: The output starts with is “3.106856”*
5656

57+
### Always-on-Top
58+
59+
**Test 1**
60+
Steps:
61+
1. Launch the "Calculator" app and navigate to "Standard" Calculator
62+
*Expected: Always-on-Top button's tooltip says "Keep on top"*
63+
2. Click the Always-on-Top button
64+
*Expected: Always-on-Top button's tooltip now says "Back to full view"*
65+
3. Launch the "Notepad" app and put it in full-screen mode
66+
*Expected: Calculator is still on top of Notepad and in Always-on-Top mode*
67+
68+
**Test 2**
69+
Steps:
70+
1. Launch the "Calculator" app and from "Standard" Calculator, input “3”, “+”, “3” (do not press “Enter”)
71+
2. Tab over the Always-on-Top button and press "Enter" on the keyboard
72+
*Expected: The application title, hamburger menu, calculator type title, calculation expression (the secondary line above the main display), history button and memory buttons are no longer visible. The main display shows "3"*
73+
2. Press “Enter”
74+
*Expected: The main display shows "6"*
75+
3. Press "Ctrl-H" on the keyboard
76+
*Expected: Nothing happens (history keyboard shortcuts are disabled)*
77+
4. Press "Ctrl-P" on the keyboard, then tab over the Always-on-Top button and press "Enter" on the keyboard again
78+
5. Open the Memory panel
79+
*Expected: Nothing is stored in memory (memory keyboard shortcuts are disabled in Always-on-Top mode) and "6" is in history*
80+
81+
**Test 3**
82+
Steps:
83+
1. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button
84+
2. Resize the window horizontally
85+
*Expected: The buttons automatically expand or shrink to fit the available screen size*
86+
3. Resize the window vertically
87+
*Expected: The buttons automatically expand or shrink to fit the available screen size and the percent, square-root, squared and reciprocal buttons disappear when the screen height is small*
88+
4. Click the Always-on-Top button again
89+
*Expected: Calculator is in Standard mode and the original window layout from before Step 1 is restored*
90+
5. Click the Always-on-Top button again
91+
*Expected: Calculator is in Always-on-Top mode and the window size from after Step 3 is restored*
92+
6. Close the "Calculator" app
93+
7. Launch the "Calculator" app again and click the Always-on-Top button
94+
*Expected: The window size from right before closing from Always-on-Top mode (ie. after Step 5) is restored*
95+
96+
**Test 4**
97+
Steps:
98+
1. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button
99+
2. Input "/", "0", “Enter” on the keyboard
100+
*Expected: "Result is undefined" is displayed in the system default app language*
101+
3. Click the Always-on-Top button again
102+
*Expected: Calculator is in Standard mode and all operator (except for "CE", "C", "Delete" and "=") and memory buttons are disabled
103+
104+
**Test 5**
105+
Steps:
106+
1. Launch the "Calculator" app and navigate to "Scientific" Calculator
107+
*Expected: The Always-on-Top button is hidden*
108+
2. Navigate to "Standard" Calculator
109+
*Expected: The Always-on-Top button is visible*
57110

58111
## Basic Verification Tests
59112

@@ -278,7 +331,7 @@ Steps:
278331
Steps:
279332
1. Launch the "Calculator" app.
280333

281-
For All Applicable Modes verify the following:
334+
For All Applicable Modes verify the following (note: only 11-15 and 20 work in Always-on-Top mode):
282335
2. Press **Alt +1** to Enter "Standard" mode
283336
*Expected: Move to "Standard" screen.*
284337
3. Press **Alt +2** to Enter "Scientific" mode
@@ -353,3 +406,30 @@ Steps:
353406
61. Press **|** to Select 'Or'
354407
62. Press **~** to Select 'Not'
355408
63. Press **&** to Select 'And'
409+
410+
## Localization Tests
411+
412+
### Always-on-Top
413+
414+
**Test 1**
415+
Steps:
416+
1. Change the system default app language to Arabic
417+
2. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button
418+
*Expected: UI/Menu is localized (for example, the title bar buttons is in right-to-left order)*
419+
3. Input "/", "0", “Enter” on the keyboard
420+
*Expected: Error message is in Arabic*
421+
422+
## Ease of Access Tests
423+
424+
### Always-on-Top
425+
426+
**Test 1**
427+
Steps:
428+
1. Open the "Narrator" app
429+
2. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button
430+
3. Tab over the Always-on-Top button
431+
*Expected: Narrator reads the localized version of "Back to full view"*
432+
4. Tab over the main results field
433+
*Expected: Narrator reads the localized version of exactly what's displayed (ie. "0")*
434+
5. Tab over the rest of the UI elements
435+
*Expected: Narrator reads the localized version of the UI elements' contents*

src/CalcViewModel/ApplicationViewModel.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ using namespace Windows::UI::Xaml::Controls;
3131
using namespace Windows::UI::Xaml::Data;
3232
using namespace Windows::UI::Xaml::Input;
3333
using namespace Windows::UI::Xaml::Media;
34+
using namespace Windows::Foundation;
35+
using namespace Concurrency;
3436

3537
namespace
3638
{
@@ -55,6 +57,7 @@ void ApplicationViewModel::Mode::set(ViewMode value)
5557
{
5658
PreviousMode = m_mode;
5759
m_mode = value;
60+
SetDisplayNormalAlwaysOnTopOption();
5861
OnModeChanged();
5962
RaisePropertyChanged(ModePropertyName);
6063
}
@@ -204,3 +207,60 @@ void ApplicationViewModel::SetMenuCategories()
204207
// property setter logic.
205208
Categories = NavCategoryGroup::CreateMenuOptions();
206209
}
210+
211+
void ApplicationViewModel::ToggleAlwaysOnTop(float width, float height)
212+
{
213+
HandleToggleAlwaysOnTop(width, height);
214+
}
215+
216+
task<void> ApplicationViewModel::HandleToggleAlwaysOnTop(float width, float height)
217+
{
218+
if (ApplicationView::GetForCurrentView()->ViewMode == ApplicationViewMode::CompactOverlay)
219+
{
220+
ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings;
221+
localSettings->Values->Insert(WidthLocalSettings, width);
222+
localSettings->Values->Insert(HeightLocalSettings, height);
223+
224+
bool success = co_await ApplicationView::GetForCurrentView()->TryEnterViewModeAsync(ApplicationViewMode::Default);
225+
CalculatorViewModel->AreHistoryShortcutsEnabled = success;
226+
CalculatorViewModel->HistoryVM->AreHistoryShortcutsEnabled = success;
227+
CalculatorViewModel->IsAlwaysOnTop = !success;
228+
IsAlwaysOnTop = !success;
229+
}
230+
else
231+
{
232+
ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings;
233+
ViewModePreferences ^ compactOptions = ViewModePreferences::CreateDefault(ApplicationViewMode::CompactOverlay);
234+
if (!localSettings->Values->GetView()->HasKey(LaunchedLocalSettings))
235+
{
236+
compactOptions->CustomSize = Size(320, 394);
237+
localSettings->Values->Insert(LaunchedLocalSettings, true);
238+
}
239+
else
240+
{
241+
if (localSettings->Values->GetView()->HasKey(WidthLocalSettings) && localSettings->Values->GetView()->HasKey(HeightLocalSettings))
242+
{
243+
float oldWidth = safe_cast<IPropertyValue ^>(localSettings->Values->GetView()->Lookup(WidthLocalSettings))->GetSingle();
244+
float oldHeight = safe_cast<IPropertyValue ^>(localSettings->Values->GetView()->Lookup(HeightLocalSettings))->GetSingle();
245+
compactOptions->CustomSize = Size(oldWidth, oldHeight);
246+
}
247+
else
248+
{
249+
compactOptions->CustomSize = Size(320, 394);
250+
}
251+
}
252+
253+
bool success = co_await ApplicationView::GetForCurrentView()->TryEnterViewModeAsync(ApplicationViewMode::CompactOverlay, compactOptions);
254+
CalculatorViewModel->AreHistoryShortcutsEnabled = !success;
255+
CalculatorViewModel->HistoryVM->AreHistoryShortcutsEnabled = !success;
256+
CalculatorViewModel->IsAlwaysOnTop = success;
257+
IsAlwaysOnTop = success;
258+
}
259+
SetDisplayNormalAlwaysOnTopOption();
260+
}
261+
262+
void ApplicationViewModel::SetDisplayNormalAlwaysOnTopOption()
263+
{
264+
DisplayNormalAlwaysOnTopOption =
265+
m_mode == ViewMode::Standard && ApplicationView::GetForCurrentView()->IsViewModeSupported(ApplicationViewMode::CompactOverlay) && !IsAlwaysOnTop;
266+
}

src/CalcViewModel/ApplicationViewModel.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ namespace CalculatorApp
2525
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, PreviousMode);
2626
OBSERVABLE_NAMED_PROPERTY_RW(Platform::String ^, CategoryName);
2727

28+
// Indicates whether calculator is currently in standard mode _and_ supports CompactOverlay _and_ is not in Always-on-Top mode
29+
OBSERVABLE_PROPERTY_RW(bool, DisplayNormalAlwaysOnTopOption);
30+
2831
COMMAND_FOR_METHOD(CopyCommand, ApplicationViewModel::OnCopyCommand);
2932
COMMAND_FOR_METHOD(PasteCommand, ApplicationViewModel::OnPasteCommand);
3033

@@ -64,6 +67,48 @@ namespace CalculatorApp
6467
}
6568
}
6669

70+
static property Platform::String ^ LaunchedLocalSettings
71+
{
72+
Platform::String ^ get()
73+
{
74+
return Platform::StringReference(L"calculatorAlwaysOnTopLaunched");
75+
}
76+
}
77+
78+
static property Platform::String ^ WidthLocalSettings
79+
{
80+
Platform::String ^ get()
81+
{
82+
return Platform::StringReference(L"calculatorAlwaysOnTopLastWidth");
83+
}
84+
}
85+
86+
static property Platform::String ^ HeightLocalSettings
87+
{
88+
Platform::String ^ get()
89+
{
90+
return Platform::StringReference(L"calculatorAlwaysOnTopLastHeight");
91+
}
92+
}
93+
94+
property bool IsAlwaysOnTop
95+
{
96+
bool get()
97+
{
98+
return m_isAlwaysOnTop;
99+
}
100+
void set(bool value)
101+
{
102+
if (m_isAlwaysOnTop != value)
103+
{
104+
m_isAlwaysOnTop = value;
105+
RaisePropertyChanged(L"IsAlwaysOnTop");
106+
}
107+
}
108+
}
109+
110+
void ToggleAlwaysOnTop(float width, float height);
111+
67112
private:
68113
bool TryRecoverFromNavigationModeFailure();
69114

@@ -76,6 +121,10 @@ namespace CalculatorApp
76121

77122
CalculatorApp::Common::ViewMode m_mode;
78123
Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup ^> ^ m_categories;
124+
Concurrency::task<void> HandleToggleAlwaysOnTop(float width, float height);
125+
void SetDisplayNormalAlwaysOnTopOption();
126+
127+
bool m_isAlwaysOnTop;
79128
};
80129
}
81130
}

src/CalcViewModel/Common/KeyboardShortcutManager.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,10 +668,11 @@ void KeyboardShortcutManager::OnAcceleratorKeyActivated(CoreDispatcher ^, Accele
668668
if (nullptr != vm)
669669
{
670670
ViewMode toMode = NavCategory::GetViewModeForVirtualKey(static_cast<MyVirtualKey>(key));
671-
if (NavCategory::IsValidViewMode(toMode))
671+
auto nvi = dynamic_cast<MUXC::NavigationViewItem ^>(menuItems->GetAt(NavCategory::GetFlatIndex(toMode)));
672+
if (nvi && nvi->IsEnabled && NavCategory::IsValidViewMode(toMode))
672673
{
673674
vm->Mode = toMode;
674-
navView->SelectedItem = menuItems->GetAt(NavCategory::GetFlatIndex(toMode));
675+
navView->SelectedItem = nvi;
675676
}
676677
}
677678
}

src/CalcViewModel/Common/TraceLogger.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ namespace CalculatorApp
117117
return true;
118118
}
119119

120-
void TraceLogger::LogVisualStateChanged(ViewMode mode, wstring_view state) const
120+
void TraceLogger::LogVisualStateChanged(ViewMode mode, wstring_view state, bool isAlwaysOnTop) const
121121
{
122122
if (!GetTraceLoggingProviderEnabled())
123123
{
@@ -128,6 +128,7 @@ namespace CalculatorApp
128128
fields.AddGuid(L"SessionGuid", sessionGuid);
129129
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
130130
fields.AddString(L"VisualState", state);
131+
fields.AddBoolean(L"IsAlwaysOnTop", isAlwaysOnTop);
131132
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
132133
LogLevel2Event(EVENT_NAME_VISUAL_STATE_CHANGED, fields);
133134
}

src/CalcViewModel/Common/TraceLogger.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ namespace CalculatorApp
4545
void LogDateCalculationModeUsed(bool AddSubtractMode);
4646
void UpdateWindowCount(size_t windowCount = 0);
4747
bool IsWindowIdInLog(int windowId);
48-
void LogVisualStateChanged(CalculatorApp::Common::ViewMode mode, std::wstring_view state) const;
48+
void LogVisualStateChanged(CalculatorApp::Common::ViewMode mode, std::wstring_view state, bool isAlwaysOnTop = false) const;
4949
void LogWindowCreated(CalculatorApp::Common::ViewMode mode, int windowId);
5050
void LogConverterInputReceived(CalculatorApp::Common::ViewMode mode) const;
5151
void LogNavBarOpened() const;

src/CalcViewModel/StandardCalculatorViewModel.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ namespace
3535
StringReference IsStandardPropertyName(L"IsStandard");
3636
StringReference IsScientificPropertyName(L"IsScientific");
3737
StringReference IsProgrammerPropertyName(L"IsProgrammer");
38+
StringReference IsAlwaysOnTopPropertyName(L"IsAlwaysOnTop");
3839
StringReference DisplayValuePropertyName(L"DisplayValue");
3940
StringReference CalculationResultAutomationNamePropertyName(L"CalculationResultAutomationName");
4041
StringReference IsBitFlipCheckedPropertyName(L"IsBitFlipChecked");
@@ -202,7 +203,12 @@ void StandardCalculatorViewModel::SetPrimaryDisplay(_In_ wstring const& displayS
202203
// not match what the narrator is saying
203204
m_CalculationResultAutomationName = CalculateNarratorDisplayValue(displayStringValue, localizedDisplayStringValue, isError);
204205

205-
DisplayValue = localizedDisplayStringValue;
206+
AreAlwaysOnTopResultsUpdated = false;
207+
if (DisplayValue != localizedDisplayStringValue)
208+
{
209+
DisplayValue = localizedDisplayStringValue;
210+
AreAlwaysOnTopResultsUpdated = true;
211+
}
206212

207213
IsInError = isError;
208214

@@ -415,7 +421,7 @@ void StandardCalculatorViewModel::SetMemorizedNumbers(const vector<wstring>& new
415421
memorySlot->Value = Utils::LRO + ref new String(stringValue.c_str()) + Utils::PDF;
416422

417423
MemorizedNumbers->InsertAt(0, memorySlot);
418-
IsMemoryEmpty = false;
424+
IsMemoryEmpty = IsAlwaysOnTop;
419425

420426
// Update the slot position for the rest of the slots
421427
for (unsigned int i = 1; i < MemorizedNumbers->Size; i++)

src/CalcViewModel/StandardCalculatorViewModel.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ namespace CalculatorApp
7878
OBSERVABLE_PROPERTY_RW(bool, IsByteEnabled);
7979
OBSERVABLE_PROPERTY_RW(int, CurrentRadixType);
8080
OBSERVABLE_PROPERTY_RW(bool, AreTokensUpdated);
81+
OBSERVABLE_PROPERTY_RW(bool, AreAlwaysOnTopResultsUpdated);
8182
OBSERVABLE_PROPERTY_RW(bool, AreHistoryShortcutsEnabled);
8283
OBSERVABLE_PROPERTY_RW(bool, AreProgrammerRadixOperatorsEnabled);
8384
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement ^, Announcement);
@@ -213,6 +214,22 @@ namespace CalculatorApp
213214
}
214215
}
215216

217+
property bool IsAlwaysOnTop
218+
{
219+
bool get()
220+
{
221+
return m_isAlwaysOnTop;
222+
}
223+
void set(bool value)
224+
{
225+
if (m_isAlwaysOnTop != value)
226+
{
227+
m_isAlwaysOnTop = value;
228+
RaisePropertyChanged(L"IsAlwaysOnTop");
229+
}
230+
}
231+
}
232+
216233
property bool IsEditingEnabled
217234
{
218235
bool get()
@@ -406,6 +423,7 @@ namespace CalculatorApp
406423
bool m_isStandard;
407424
bool m_isScientific;
408425
bool m_isProgrammer;
426+
bool m_isAlwaysOnTop;
409427
bool m_isBinaryBitFlippingEnabled;
410428
bool m_isBitFlipChecked;
411429
bool m_isShiftChecked;

src/CalcViewModel/pch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <concrt.h>
3131
#include <regex>
3232
#include <iterator>
33+
#include <intsafe.h>
3334
// C++\WinRT Headers
3435
#include "winrt/base.h"
3536
#include "winrt/Windows.Foundation.Diagnostics.h"

src/Calculator/App.xaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@
128128
<x:Double x:Key="CalcStandardOperatorCaptionSizeLarge">24</x:Double>
129129
<!-- Numpad Standard/Scientific in Fill/Full -->
130130
<x:Double x:Key="CalcStandardOperatorCaptionSize">20</x:Double>
131+
<x:Double x:Key="CalcStandardOperatorCaptionSizeSmall">15</x:Double>
132+
<x:Double x:Key="CalcStandardOperatorCaptionSizeTiny">12</x:Double>
131133
<!-- Standard Operators Standard/Scientific in Fill/Full -->
132134
<x:Double x:Key="CalcOperatorCaptionSize">15</x:Double>
133135

@@ -210,11 +212,21 @@
210212
TargetType="Controls:CalculatorButton">
211213
<Setter Property="FontWeight" Value="SemiBold"/>
212214
</Style>
215+
<Style x:Key="NumericButtonStyle10"
216+
BasedOn="{StaticResource NumericButtonStyle}"
217+
TargetType="Controls:CalculatorButton">
218+
<Setter Property="FontSize" Value="10"/>
219+
</Style>
213220
<Style x:Key="NumericButtonStyle12"
214221
BasedOn="{StaticResource NumericButtonStyle}"
215222
TargetType="Controls:CalculatorButton">
216223
<Setter Property="FontSize" Value="12"/>
217224
</Style>
225+
<Style x:Key="NumericButtonStyle16"
226+
BasedOn="{StaticResource NumericButtonStyle}"
227+
TargetType="Controls:CalculatorButton">
228+
<Setter Property="FontSize" Value="16"/>
229+
</Style>
218230
<Style x:Key="NumericButtonStyle18"
219231
BasedOn="{StaticResource NumericButtonStyle}"
220232
TargetType="Controls:CalculatorButton">

0 commit comments

Comments
 (0)