diff --git a/src/Calculator/Controls/CalculationResult.cpp b/src/Calculator/Controls/CalculationResult.cpp index e6024df5..9c9d5785 100644 --- a/src/Calculator/Controls/CalculationResult.cpp +++ b/src/Calculator/Controls/CalculationResult.cpp @@ -47,6 +47,11 @@ DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, DisplayStringExpression); #define WIDTHCUTOFF 50 #define FONTTOLERANCE 0.001 +// We need a safety margin to guarantee we correctly always show/hide ScrollLeft and ScrollRight buttons when necessary. +// In rare cases, ScrollViewer::HorizontalOffset is a little low by a few (sub)pixels when users scroll to one of the extremity +// and no events are launched when they scroll again in the same direction +#define SCROLL_BUTTONS_APPROXIMATION_RANGE 4 + StringReference CalculationResult::s_FocusedState(L"Focused"); StringReference CalculationResult::s_UnfocusedState(L"Unfocused"); @@ -67,49 +72,73 @@ Platform::String ^ CalculationResult::GetRawDisplayValue() void CalculationResult::OnApplyTemplate() { - assert((m_scrollLeft == nullptr && m_scrollRight == nullptr) || (m_scrollLeft != nullptr && m_scrollRight != nullptr)); if (m_textContainer) { - m_textContainer->LayoutUpdated -= m_textContainerLayoutChangedToken; + if (m_textContainerLayoutChangedToken.Value != 0) + { + m_textContainer->LayoutUpdated -= m_textContainerLayoutChangedToken; + m_textContainerLayoutChangedToken.Value = 0; + } + if (m_textContainerSizeChangedToken.Value != 0) + { + m_textContainer->SizeChanged -= m_textContainerSizeChangedToken; + m_textContainerSizeChangedToken.Value = 0; + } + if (m_textContainerViewChangedToken.Value != 0) + { + m_textContainer->ViewChanged -= m_textContainerViewChangedToken; + m_textContainerViewChangedToken.Value = 0; + } } + + if (m_scrollLeft != nullptr && m_scrollLeftClickToken.Value != 0) + { + m_scrollLeft->Click -= m_scrollLeftClickToken; + m_scrollLeftClickToken.Value = 0; + } + + if (m_scrollRight != nullptr && m_scrollRightClickToken.Value != 0) + { + m_scrollRight->Click -= m_scrollRightClickToken; + m_scrollRightClickToken.Value = 0; + } + m_textContainer = dynamic_cast(GetTemplateChild("TextContainer")); if (m_textContainer) { - m_textContainer->SizeChanged += ref new SizeChangedEventHandler(this, &CalculationResult::TextContainerSizeChanged); // We want to know when the size of the container changes so // we can rescale the textbox + m_textContainerSizeChangedToken = m_textContainer->SizeChanged += ref new SizeChangedEventHandler(this, &CalculationResult::OnTextContainerSizeChanged); + + m_textContainerViewChangedToken = m_textContainer->ViewChanged += + ref new Windows::Foundation::EventHandler( + this, &CalculatorApp::Controls::CalculationResult::OnTextContainerOnViewChanged); + m_textContainerLayoutChangedToken = m_textContainer->LayoutUpdated += ref new EventHandler(this, &CalculationResult::OnTextContainerLayoutUpdated); m_textContainer->ChangeView(m_textContainer->ExtentWidth - m_textContainer->ViewportWidth, nullptr, nullptr); m_scrollLeft = dynamic_cast(GetTemplateChild("ScrollLeft")); - m_scrollRight = dynamic_cast(GetTemplateChild("ScrollRight")); - auto borderContainer = dynamic_cast(GetTemplateChild("Border")); - if (m_scrollLeft && m_scrollRight) + if (m_scrollLeft) { - m_scrollLeft->Click += ref new RoutedEventHandler(this, &CalculationResult::OnScrollClick); - m_scrollRight->Click += ref new RoutedEventHandler(this, &CalculationResult::OnScrollClick); - borderContainer->PointerEntered += ref new PointerEventHandler(this, &CalculationResult::OnPointerEntered); - borderContainer->PointerExited += ref new PointerEventHandler(this, &CalculationResult::OnPointerExited); + m_scrollLeftClickToken = m_scrollLeft->Click += ref new RoutedEventHandler(this, &CalculationResult::OnScrollClick); } - m_textBlock = dynamic_cast(m_textContainer->FindName("NormalOutput")); + m_scrollRight = dynamic_cast(GetTemplateChild("ScrollRight")); + if (m_scrollRight) + { + m_scrollRightClickToken = m_scrollRight->Click += ref new RoutedEventHandler(this, &CalculationResult::OnScrollClick); + } + m_textBlock = dynamic_cast(GetTemplateChild("NormalOutput")); if (m_textBlock) { m_textBlock->Visibility = ::Visibility::Visible; } } - UpdateAllState(); + UpdateVisualState(); + UpdateTextState(); VisualStateManager::GoToState(this, s_UnfocusedState, false); } -void CalculationResult::OnPointerPressed(PointerRoutedEventArgs ^ e) -{ - if (m_scrollLeft && m_scrollRight && e->Pointer->PointerDeviceType == PointerDeviceType::Touch) - { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Collapsed); - } -} - void CalculationResult::OnTextContainerLayoutUpdated(Object ^ /*sender*/, Object ^ /*e*/) { if (m_isScalingText) @@ -118,7 +147,7 @@ void CalculationResult::OnTextContainerLayoutUpdated(Object ^ /*sender*/, Object } } -void CalculationResult::TextContainerSizeChanged(Object ^ /*sender*/, SizeChangedEventArgs ^ /*e*/) +void CalculationResult::OnTextContainerSizeChanged(Object ^ /*sender*/, SizeChangedEventArgs ^ /*e*/) { UpdateTextState(); } @@ -248,64 +277,46 @@ void CalculationResult::UpdateTextState() { m_textContainer->ChangeView(m_textContainer->ExtentWidth - m_textContainer->ViewportWidth, nullptr, nullptr); } - - if (m_scrollLeft && m_scrollRight) - { - if (m_textBlock->ActualWidth < containerSize) - { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Collapsed); - } - else - { - if (IsOperatorCommand) - { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Visible); - } - else - { - ShowHideScrollButtons(::Visibility::Visible, ::Visibility::Collapsed); - } - } - } - m_textBlock->InvalidateArrange(); } } void CalculationResult::ScrollLeft() { + if (m_textContainer == nullptr) + { + return; + } if (m_textContainer->HorizontalOffset > 0) { double offset = m_textContainer->HorizontalOffset - (scrollRatio * m_textContainer->ViewportWidth); m_textContainer->ChangeView(offset, nullptr, nullptr); - m_textContainer->UpdateLayout(); - UpdateScrollButtons(); } } void CalculationResult::ScrollRight() { + if (m_textContainer == nullptr) + { + return; + } + if (m_textContainer->HorizontalOffset < m_textContainer->ExtentWidth - m_textContainer->ViewportWidth) { double offset = m_textContainer->HorizontalOffset + (scrollRatio * m_textContainer->ViewportWidth); m_textContainer->ChangeView(offset, nullptr, nullptr); - m_textContainer->UpdateLayout(); - UpdateScrollButtons(); } } void CalculationResult::OnKeyDown(KeyRoutedEventArgs ^ e) { - if (m_scrollLeft != nullptr && m_scrollRight != nullptr) + switch (e->Key) { - auto key = e->Key; - if (key == Windows::System::VirtualKey::Left) - { - this->ScrollLeft(); - } - else if (key == Windows::System::VirtualKey::Right) - { - this->ScrollRight(); - } + case Windows::System::VirtualKey::Left: + this->ScrollLeft(); + break; + case Windows::System::VirtualKey::Right: + this->ScrollRight(); + break; } } @@ -322,48 +333,24 @@ void CalculationResult::OnScrollClick(Object ^ sender, RoutedEventArgs ^ /*e*/) } } -void CalculationResult::OnPointerEntered(Platform::Object ^ sender, PointerRoutedEventArgs ^ e) -{ - if (e->Pointer->PointerDeviceType == PointerDeviceType::Mouse && m_textBlock->ActualWidth >= m_textContainer->ActualWidth) - { - UpdateScrollButtons(); - } -} - -void CalculationResult::ShowHideScrollButtons(::Visibility vLeft, ::Visibility vRight) -{ - m_scrollLeft->Visibility = vLeft; - m_scrollRight->Visibility = vRight; -} - void CalculationResult::UpdateScrollButtons() { - // When the width is smaller than the container, don't show any - if (m_textBlock->ActualWidth < m_textContainer->ActualWidth) + if (m_textContainer == nullptr) { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Collapsed); + return; } - // We have more number on both side. Show both arrows - else if (m_textContainer->HorizontalOffset > 0 && m_textContainer->HorizontalOffset < (m_textContainer->ExtentWidth - m_textContainer->ViewportWidth)) - { - ShowHideScrollButtons(::Visibility::Visible, ::Visibility::Visible); - } - // Width is larger than the container and left most part of the number is shown. Should be able to scroll left. - else if (m_textContainer->HorizontalOffset == 0) - { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Visible); - } - else // Width is larger than the container and right most part of the number is shown. Should be able to scroll left. - { - ShowHideScrollButtons(::Visibility::Visible, ::Visibility::Collapsed); - } -} -void CalculationResult::OnPointerExited(Platform::Object ^ sender, PointerRoutedEventArgs ^ e) -{ - if (e->Pointer->PointerDeviceType == PointerDeviceType::Mouse) + if (m_scrollLeft != nullptr) { - UpdateScrollButtons(); + m_scrollLeft->Visibility = m_textContainer->HorizontalOffset > SCROLL_BUTTONS_APPROXIMATION_RANGE ? ::Visibility::Visible : ::Visibility::Collapsed; + } + + if (m_scrollRight != nullptr) + { + m_scrollRight->Visibility = + m_textContainer->HorizontalOffset + m_textContainer->ViewportWidth + SCROLL_BUTTONS_APPROXIMATION_RANGE < m_textContainer->ExtentWidth + ? ::Visibility::Visible + : ::Visibility::Collapsed; } } @@ -382,12 +369,6 @@ void CalculationResult::ModifyFontAndMargin(TextBlock ^ textBox, double fontChan textBox->FontSize = newFontSize; } -void CalculationResult::UpdateAllState() -{ - UpdateVisualState(); - UpdateTextState(); -} - void CalculationResult::OnTapped(TappedRoutedEventArgs ^ e) { this->Focus(::FocusState::Programmatic); @@ -426,3 +407,8 @@ void CalculationResult::RaiseSelectedEvent() { Selected(this); } + +void CalculationResult::OnTextContainerOnViewChanged(Object ^ /*sender*/, ScrollViewerViewChangedEventArgs ^ e) +{ + UpdateScrollButtons(); +} diff --git a/src/Calculator/Controls/CalculationResult.h b/src/Calculator/Controls/CalculationResult.h index b160d822..32910c21 100644 --- a/src/Calculator/Controls/CalculationResult.h +++ b/src/Calculator/Controls/CalculationResult.h @@ -42,7 +42,6 @@ namespace CalculatorApp virtual void OnKeyDown(Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e) override; virtual void OnApplyTemplate() override; virtual void OnTapped(Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e) override; - virtual void OnPointerPressed(Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e) override; virtual void OnRightTapped(Windows::UI::Xaml::Input::RightTappedRoutedEventArgs ^ e) override; virtual void OnGotFocus(Windows::UI::Xaml::RoutedEventArgs ^ e) override; virtual void OnLostFocus(Windows::UI::Xaml::RoutedEventArgs ^ e) override; @@ -56,15 +55,12 @@ namespace CalculatorApp void OnIsInErrorPropertyChanged(bool oldValue, bool newValue); void OnMinFontSizePropertyChanged(double oldValue, double newValue); void OnMaxFontSizePropertyChanged(double oldValue, double newValue); - void TextContainerSizeChanged(Object ^ sender, Windows::UI::Xaml::SizeChangedEventArgs ^ e); + void OnTextContainerSizeChanged(Object ^ sender, Windows::UI::Xaml::SizeChangedEventArgs ^ e); void OnTextContainerLayoutUpdated(Object ^ sender, Object ^ e); + void OnTextContainerOnViewChanged(Object ^ sender, Windows::UI::Xaml::Controls::ScrollViewerViewChangedEventArgs ^ e); void UpdateVisualState(); - void UpdateAllState(); void OnScrollClick(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); - void OnPointerEntered(Platform::Object ^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e); - void OnPointerExited(Platform::Object ^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e); void ModifyFontAndMargin(Windows::UI::Xaml::Controls::TextBlock ^ textBlock, double fontChange); - void ShowHideScrollButtons(Windows::UI::Xaml::Visibility vLeft, Windows::UI::Xaml::Visibility vRight); void UpdateScrollButtons(); void ScrollLeft(); void ScrollRight(); @@ -82,6 +78,10 @@ namespace CalculatorApp bool m_isScalingText; bool m_haveCalculatedMax; Windows::Foundation::EventRegistrationToken m_textContainerLayoutChangedToken; + Windows::Foundation::EventRegistrationToken m_textContainerViewChangedToken; + Windows::Foundation::EventRegistrationToken m_textContainerSizeChangedToken; + Windows::Foundation::EventRegistrationToken m_scrollRightClickToken; + Windows::Foundation::EventRegistrationToken m_scrollLeftClickToken; }; } }