From ef3f5e9cbbf53017e1babdcf15ca9c7fc7eb7156 Mon Sep 17 00:00:00 2001 From: Rudy Huyn Date: Tue, 26 Mar 2019 11:24:36 -0700 Subject: [PATCH] Improve performance of SupplementaryResult + Modify the height of RowDltrUnits when UnitConverter is in Landscape (#249) * Modify the height of RowDltrUnits when UnitConverter is in LandscapeLayout mode * clean * Use the same layout than the existing one while fixing the issue * Refactor SupplementaryItemsControl to improve performance, not rely on parents and not force the parent element to be HorizonAlignment="stretch" * take feedback into account * add HorizontalNoOverflowStackPanel to vcproj.filters * format conditionals * replace max by std::max --- src/Calculator/Calculator.vcxproj | 2 + src/Calculator/Calculator.vcxproj.filters | 6 ++ .../HorizontalNoOverflowStackPanel.cpp | 70 ++++++++++++++++++ .../Controls/HorizontalNoOverflowStackPanel.h | 28 +++++++ .../Controls/SupplementaryItemsControl.cpp | 2 - .../Views/SupplementaryResults.xaml | 4 +- .../Views/SupplementaryResults.xaml.cpp | 74 +++---------------- .../Views/SupplementaryResults.xaml.h | 17 +++-- src/Calculator/Views/UnitConverter.xaml | 21 ++++-- src/Calculator/Views/UnitConverter.xaml.cpp | 8 ++ src/Calculator/Views/UnitConverter.xaml.h | 1 + 11 files changed, 151 insertions(+), 82 deletions(-) create mode 100644 src/Calculator/Controls/HorizontalNoOverflowStackPanel.cpp create mode 100644 src/Calculator/Controls/HorizontalNoOverflowStackPanel.h diff --git a/src/Calculator/Calculator.vcxproj b/src/Calculator/Calculator.vcxproj index e0a757f..4e19aab 100644 --- a/src/Calculator/Calculator.vcxproj +++ b/src/Calculator/Calculator.vcxproj @@ -251,6 +251,7 @@ + App.xaml @@ -384,6 +385,7 @@ + Create Create diff --git a/src/Calculator/Calculator.vcxproj.filters b/src/Calculator/Calculator.vcxproj.filters index 2cd7dcd..cc25a22 100644 --- a/src/Calculator/Calculator.vcxproj.filters +++ b/src/Calculator/Calculator.vcxproj.filters @@ -308,6 +308,9 @@ Common + + Controls + @@ -407,6 +410,9 @@ Common + + Controls + diff --git a/src/Calculator/Controls/HorizontalNoOverflowStackPanel.cpp b/src/Calculator/Controls/HorizontalNoOverflowStackPanel.cpp new file mode 100644 index 0000000..b009bc2 --- /dev/null +++ b/src/Calculator/Controls/HorizontalNoOverflowStackPanel.cpp @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// +// HorizontalNoOverflowStackPanel.xaml.cpp +// Implementation of the HorizontalNoOverflowStackPanel class +// + +#include "pch.h" +#include "HorizontalNoOverflowStackPanel.h" + +using namespace std; +using namespace CalculatorApp::Controls; +using namespace Windows::Foundation; + +Size HorizontalNoOverflowStackPanel::MeasureOverride(Size availableSize) +{ + float maxHeight = 0; + float width = 0; + for (auto child : Children) + { + child->Measure(Size(numeric_limits::infinity(), numeric_limits::infinity())); + maxHeight = max(maxHeight, child->DesiredSize.Height); + width += child->DesiredSize.Width; + } + return Size(min(width, availableSize.Width), min(availableSize.Height, maxHeight)); +} + +bool HorizontalNoOverflowStackPanel::ShouldPrioritizeLastItem() +{ + return false; +} + +Size HorizontalNoOverflowStackPanel::ArrangeOverride(Size finalSize) +{ + if (Children->Size == 0) + { + return finalSize; + } + + float posX = 0; + auto lastChild = Children->GetAt(Children->Size - 1); + float lastChildWidth = 0; + if (Children->Size > 2 && ShouldPrioritizeLastItem()) + { + lastChildWidth = lastChild->DesiredSize.Width; + } + for (auto item : Children) + { + auto widthAvailable = finalSize.Width - posX; + if (item != lastChild) + { + widthAvailable -= lastChildWidth; + } + float itemWidth = item->DesiredSize.Width; + if (widthAvailable > 0 && itemWidth <= widthAvailable) + { + //stack the items horizontally (left to right) + item->Arrange(Rect(posX, 0, itemWidth, finalSize.Height)); + posX += item->RenderSize.Width; + } + else + { + //Not display the item + item->Arrange(Rect(0, 0, 0, 0)); + } + } + return finalSize; +} + diff --git a/src/Calculator/Controls/HorizontalNoOverflowStackPanel.h b/src/Calculator/Controls/HorizontalNoOverflowStackPanel.h new file mode 100644 index 0000000..7619faf --- /dev/null +++ b/src/Calculator/Controls/HorizontalNoOverflowStackPanel.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// +// HorizontalNoOverflowStackPanel.h +// Declaration of the HorizontalNoOverflowStackPanel class +// + +#pragma once +#include "CalcViewModel/Common/Utils.h" + +namespace CalculatorApp +{ + namespace Controls + { + public ref class HorizontalNoOverflowStackPanel : public Windows::UI::Xaml::Controls::Panel + { + DEPENDENCY_PROPERTY_OWNER(HorizontalNoOverflowStackPanel); + //Prioritize the last item over all other items (except the first one) + internal: + HorizontalNoOverflowStackPanel() {} + protected: + virtual Windows::Foundation::Size MeasureOverride(Windows::Foundation::Size availableSize) override; + virtual Windows::Foundation::Size ArrangeOverride(Windows::Foundation::Size finalSize) override; + virtual bool ShouldPrioritizeLastItem(); + }; + } +} diff --git a/src/Calculator/Controls/SupplementaryItemsControl.cpp b/src/Calculator/Controls/SupplementaryItemsControl.cpp index d5bc00a..665c1d1 100644 --- a/src/Calculator/Controls/SupplementaryItemsControl.cpp +++ b/src/Calculator/Controls/SupplementaryItemsControl.cpp @@ -38,5 +38,3 @@ void SupplementaryItemsControl::PrepareContainerForItemOverride(DependencyObject { return ref new SupplementaryContentPresenterAP(this); } - - diff --git a/src/Calculator/Views/SupplementaryResults.xaml b/src/Calculator/Views/SupplementaryResults.xaml index 9ac6421..c3db4f4 100644 --- a/src/Calculator/Views/SupplementaryResults.xaml +++ b/src/Calculator/Views/SupplementaryResults.xaml @@ -80,7 +80,7 @@ - + @@ -102,6 +102,6 @@ Style="{ThemeResource SupplementaryValuesStyle}" IsTextScaleFactorEnabled="False" ItemTemplateSelector="{StaticResource ResultTemplateSelector}" - LayoutUpdated="OnSupplementaryValuesLayoutUpdated"/> + ItemsSource="{x:Bind Results, Mode=OneWay}"/> diff --git a/src/Calculator/Views/SupplementaryResults.xaml.cpp b/src/Calculator/Views/SupplementaryResults.xaml.cpp index 978df9a..06c8360 100644 --- a/src/Calculator/Views/SupplementaryResults.xaml.cpp +++ b/src/Calculator/Views/SupplementaryResults.xaml.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // @@ -27,6 +27,8 @@ using namespace Windows::UI::Xaml::Navigation; // The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 +DEPENDENCY_PROPERTY_INITIALIZATION(SupplementaryResults, Results); + Object^ DelighterUnitToStyleConverter::Convert(Object^ value, TypeName /*targetType*/, Object^ /*parameter*/, String^ /*language*/) { Unit^ unit = safe_cast(value); @@ -62,74 +64,22 @@ Windows::UI::Xaml::DataTemplate^ SupplementaryResultDataTemplateSelector::Select } } -SupplementaryResults::SupplementaryResults() : - m_data(ref new Vector) +SupplementaryResults::SupplementaryResults() { InitializeComponent(); - - this->Loaded += ref new RoutedEventHandler(this, &SupplementaryResults::OnLoaded); } -void SupplementaryResults::RefreshData() +bool SupplementaryResultNoOverflowStackPanel::ShouldPrioritizeLastItem() { - // Copy the list so that when we chop stuff off, we don't modify the original - // complete list. - m_data->Clear(); - for(SupplementaryResult^ sr : safe_cast(this->DataContext)->SupplementaryResults) + if (Children->Size == 0) { - m_data->Append(sr); + return false; } - - // Set as source - SupplementaryValues->ItemsSource = m_data; -} - -void SupplementaryResults::OnLoaded(Object^ sender, RoutedEventArgs^ e) -{ - UnitConverterViewModel^ vm = safe_cast(this->DataContext); - vm->PropertyChanged += ref new PropertyChangedEventHandler(this, &SupplementaryResults::OnConverterPropertyChanged); - Window::Current->SizeChanged += ref new WindowSizeChangedEventHandler(this, &SupplementaryResults::OnWindowSizeChanged); - // We may be loaded into a state where we need to render (like rehydrate), so prepare data - RefreshData(); -} - -void SupplementaryResults::OnConverterPropertyChanged(Object^ /*sender*/, PropertyChangedEventArgs^ e) -{ - if (e->PropertyName == UnitConverterViewModel::SupplementaryResultsPropertyName) + auto lastChild = dynamic_cast(Children->GetAt(Children->Size - 1)); + if (lastChild == nullptr) { - RefreshData(); - } -} - -void SupplementaryResults::OnWindowSizeChanged(Platform::Object^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ e) -{ - // to reload supplementary results every time the window is resized - RefreshData(); -} - -void SupplementaryResults::OnSupplementaryValuesLayoutUpdated(Platform::Object^ sender, Platform::Object^ e) -{ - // This means we overflowed and are cutting off, or in a very rare case we fit exactly. Unfortunately - // the fitting exactly case will still have an item removed, as there is no other way for us to - // detect that we need to trim. - Grid^ parentGrid = dynamic_cast(VisualTreeHelper::GetParent(this)); - if (parentGrid != nullptr) - { - double parentWidth = parentGrid->ActualWidth; - if (SupplementaryValues && SupplementaryValues->ActualWidth >= parentWidth) - { - if (m_data->Size > 0) - { - SupplementaryResult^ last = m_data->GetAt(m_data->Size - 1); - if (!last->IsWhimsical() || m_data->Size <= 2) - { - m_data->RemoveAtEnd(); - } - else - { - m_data->RemoveAt(m_data->Size - 2); - } - } - } + return false; } + auto suppResult = dynamic_cast(lastChild->DataContext); + return suppResult == nullptr? false: suppResult->IsWhimsical(); } diff --git a/src/Calculator/Views/SupplementaryResults.xaml.h b/src/Calculator/Views/SupplementaryResults.xaml.h index 1d94b0a..a0be9e0 100644 --- a/src/Calculator/Views/SupplementaryResults.xaml.h +++ b/src/Calculator/Views/SupplementaryResults.xaml.h @@ -12,6 +12,7 @@ #include "Controls/SupplementaryItemsControl.h" #include "Controls/OperandTextBox.h" #include "Controls/OperatorTextBox.h" +#include "Controls/HorizontalNoOverflowStackPanel.h" #include "CalcViewModel/UnitConverterViewModel.h" namespace CalculatorApp @@ -58,18 +59,18 @@ namespace CalculatorApp Windows::UI::Xaml::DataTemplate^ m_delighterTemplate; }; + public ref class SupplementaryResultNoOverflowStackPanel sealed: public CalculatorApp::Controls::HorizontalNoOverflowStackPanel + { + protected: + virtual bool ShouldPrioritizeLastItem() override; + }; + [Windows::Foundation::Metadata::WebHostHidden] public ref class SupplementaryResults sealed { public: SupplementaryResults(); - - private: - void RefreshData(); - void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void OnConverterPropertyChanged(Platform::Object^ sender, Windows::UI::Xaml::Data::PropertyChangedEventArgs^ e); - void OnSupplementaryValuesLayoutUpdated(Platform::Object^ sender, Platform::Object^ e); - void OnWindowSizeChanged(Platform::Object^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ e); - Windows::Foundation::Collections::IObservableVector^ m_data; + DEPENDENCY_PROPERTY_OWNER(SupplementaryResults); + DEPENDENCY_PROPERTY_WITH_DEFAULT(Windows::Foundation::Collections::IIterable^, Results, nullptr); }; } diff --git a/src/Calculator/Views/UnitConverter.xaml b/src/Calculator/Views/UnitConverter.xaml index ab88643..e21dd50 100644 --- a/src/Calculator/Views/UnitConverter.xaml +++ b/src/Calculator/Views/UnitConverter.xaml @@ -1,4 +1,4 @@ - - + @@ -651,7 +656,7 @@ - + IsActive = false; } + +// The function will make sure the UI will have enough space to display supplementary results and currency information +void CalculatorApp::UnitConverter::SupplementaryResultsPanelInGrid_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e) +{ + //We add 0.01 to be sure to not create an infinite loop with SizeChanged events cascading due to float approximation + RowDltrUnits->MinHeight = max(48.0, e->NewSize.Height + 0.01); +} diff --git a/src/Calculator/Views/UnitConverter.xaml.h b/src/Calculator/Views/UnitConverter.xaml.h index b4b8444..df370ea 100644 --- a/src/Calculator/Views/UnitConverter.xaml.h +++ b/src/Calculator/Views/UnitConverter.xaml.h @@ -88,5 +88,6 @@ namespace CalculatorApp Windows::UI::Xaml::DispatcherTimer^ m_delayTimer; bool m_isAnimationEnabled; + void SupplementaryResultsPanelInGrid_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); }; }