241 lines
8.7 KiB
C++
241 lines
8.7 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
#include "pch.h"
|
|
#include "OverflowTextBlock.h"
|
|
#include "OverflowTextBlockAutomationPeer.h"
|
|
|
|
using namespace CalculatorApp;
|
|
using namespace CalculatorApp::Common;
|
|
using namespace CalculatorApp::Controls;
|
|
|
|
using namespace Platform;
|
|
using namespace std;
|
|
using namespace Windows::Foundation;
|
|
using namespace Windows::Foundation::Collections;
|
|
using namespace Windows::Devices::Input;
|
|
using namespace Windows::UI::Xaml;
|
|
using namespace Windows::UI::Xaml::Automation;
|
|
using namespace Windows::UI::Xaml::Automation::Peers;
|
|
using namespace Windows::UI::Xaml::Controls;
|
|
using namespace Windows::UI::Xaml::Controls::Primitives;
|
|
using namespace Windows::UI::Xaml::Data;
|
|
using namespace Windows::UI::Xaml::Input;
|
|
using namespace Windows::UI::Xaml::Media;
|
|
using namespace Windows::UI::Xaml::Navigation;
|
|
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(OverflowTextBlock, IsActive);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(OverflowTextBlock, TextStyle);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(OverflowTextBlock, TokensUpdated);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(OverflowTextBlock, ScrollButtonsWidth);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(OverflowTextBlock, ScrollButtonsFontSize);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(OverflowTextBlock, ScrollButtonsPlacement);
|
|
|
|
static constexpr unsigned int SCROLL_BUTTONS_APPROXIMATION_RANGE = 4;
|
|
static constexpr double SCROLL_RATIO = 0.7;
|
|
|
|
void OverflowTextBlock::OnApplyTemplate()
|
|
{
|
|
UnregisterEventHandlers();
|
|
|
|
auto uiElement = GetTemplateChild("ExpressionContainer");
|
|
if (uiElement != nullptr)
|
|
{
|
|
m_expressionContainer = safe_cast<ScrollViewer ^>(uiElement);
|
|
m_expressionContainer->ChangeView(m_expressionContainer->ExtentWidth - m_expressionContainer->ViewportWidth, nullptr, nullptr);
|
|
m_containerViewChangedToken = m_expressionContainer->ViewChanged +=
|
|
ref new EventHandler<ScrollViewerViewChangedEventArgs ^>(this, &OverflowTextBlock::OnViewChanged);
|
|
}
|
|
|
|
uiElement = GetTemplateChild("ExpressionContent");
|
|
if (uiElement != nullptr)
|
|
{
|
|
m_expressionContent = safe_cast<FrameworkElement ^>(uiElement);
|
|
}
|
|
|
|
uiElement = GetTemplateChild("ScrollLeft");
|
|
if (uiElement != nullptr)
|
|
{
|
|
m_scrollLeft = safe_cast<Button ^>(uiElement);
|
|
m_scrollLeftClickEventToken = m_scrollLeft->Click += ref new RoutedEventHandler(this, &OverflowTextBlock::OnScrollLeftClick);
|
|
}
|
|
|
|
uiElement = GetTemplateChild("ScrollRight");
|
|
if (uiElement != nullptr)
|
|
{
|
|
m_scrollRight = safe_cast<Button ^>(uiElement);
|
|
m_scrollRightClickEventToken = m_scrollRight->Click += ref new RoutedEventHandler(this, &OverflowTextBlock::OnScrollRightClick);
|
|
}
|
|
|
|
uiElement = GetTemplateChild("TokenList");
|
|
if (uiElement != nullptr)
|
|
{
|
|
m_itemsControl = safe_cast<ItemsControl ^>(uiElement);
|
|
}
|
|
|
|
UpdateAllState();
|
|
}
|
|
|
|
AutomationPeer ^ OverflowTextBlock::OnCreateAutomationPeer()
|
|
{
|
|
return ref new OverflowTextBlockAutomationPeer(this);
|
|
}
|
|
|
|
void OverflowTextBlock::OnTokensUpdatedPropertyChanged(bool /*oldValue*/, bool newValue)
|
|
{
|
|
if (m_expressionContainer != nullptr && newValue)
|
|
{
|
|
m_expressionContainer->UpdateLayout();
|
|
m_expressionContainer->ChangeView(m_expressionContainer->ScrollableWidth, nullptr, nullptr, true);
|
|
}
|
|
auto newIsAccessibilityViewControl = m_itemsControl != nullptr && m_itemsControl->Items->Size > 0;
|
|
if (m_isAccessibilityViewControl != newIsAccessibilityViewControl)
|
|
{
|
|
m_isAccessibilityViewControl = newIsAccessibilityViewControl;
|
|
AutomationProperties::SetAccessibilityView(this, newIsAccessibilityViewControl ? AccessibilityView::Control : AccessibilityView::Raw);
|
|
}
|
|
UpdateScrollButtons();
|
|
}
|
|
|
|
void OverflowTextBlock::UpdateAllState()
|
|
{
|
|
UpdateVisualState();
|
|
}
|
|
|
|
void OverflowTextBlock::UpdateVisualState()
|
|
{
|
|
if (IsActive)
|
|
{
|
|
VisualStateManager::GoToState(this, "Active", true);
|
|
}
|
|
else
|
|
{
|
|
VisualStateManager::GoToState(this, "Normal", true);
|
|
}
|
|
}
|
|
|
|
void OverflowTextBlock::ScrollLeft()
|
|
{
|
|
if (m_expressionContainer != nullptr && m_expressionContainer->HorizontalOffset > 0)
|
|
{
|
|
double offset = m_expressionContainer->HorizontalOffset - (SCROLL_RATIO * m_expressionContainer->ViewportWidth);
|
|
m_expressionContainer->ChangeView(offset, nullptr, nullptr);
|
|
m_expressionContainer->UpdateLayout();
|
|
UpdateScrollButtons();
|
|
}
|
|
}
|
|
|
|
void OverflowTextBlock::ScrollRight()
|
|
{
|
|
auto realOffset = m_expressionContainer->HorizontalOffset + m_expressionContainer->Padding.Left + m_expressionContent->Margin.Left;
|
|
if (m_expressionContainer != nullptr && realOffset + m_expressionContainer->ActualWidth < m_expressionContent->ActualWidth)
|
|
{
|
|
double offset = m_expressionContainer->HorizontalOffset + (SCROLL_RATIO * m_expressionContainer->ViewportWidth);
|
|
m_expressionContainer->ChangeView(offset, nullptr, nullptr);
|
|
m_expressionContainer->UpdateLayout();
|
|
UpdateScrollButtons();
|
|
}
|
|
}
|
|
|
|
void OverflowTextBlock::OnScrollLeftClick(_In_ Object ^ sender, _In_ RoutedEventArgs ^)
|
|
{
|
|
ScrollLeft();
|
|
}
|
|
|
|
void OverflowTextBlock::OnScrollRightClick(_In_ Object ^ sender, _In_ RoutedEventArgs ^)
|
|
{
|
|
ScrollRight();
|
|
}
|
|
|
|
void OverflowTextBlock::UpdateScrollButtons()
|
|
{
|
|
if (m_expressionContainer == nullptr || m_scrollLeft == nullptr || m_scrollRight == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto realOffset = m_expressionContainer->HorizontalOffset + m_expressionContainer->Padding.Left + m_expressionContent->Margin.Left;
|
|
auto scrollLeftVisibility = realOffset > SCROLL_BUTTONS_APPROXIMATION_RANGE ? ::Visibility::Visible : ::Visibility::Collapsed;
|
|
auto scrollRightVisibility = realOffset + m_expressionContainer->ActualWidth + SCROLL_BUTTONS_APPROXIMATION_RANGE < m_expressionContent->ActualWidth
|
|
? ::Visibility::Visible
|
|
: ::Visibility::Collapsed;
|
|
|
|
bool shouldTryFocusScrollRight = false;
|
|
if (m_scrollLeft->Visibility != scrollLeftVisibility)
|
|
{
|
|
if (scrollLeftVisibility == ::Visibility::Collapsed)
|
|
{
|
|
shouldTryFocusScrollRight = m_scrollLeft->Equals(FocusManager::GetFocusedElement());
|
|
}
|
|
|
|
m_scrollLeft->Visibility = scrollLeftVisibility;
|
|
}
|
|
|
|
if (m_scrollRight->Visibility != scrollRightVisibility)
|
|
{
|
|
if (scrollRightVisibility == ::Visibility::Collapsed && m_scrollLeft->Visibility == ::Visibility::Visible
|
|
&& m_scrollRight->Equals(FocusManager::GetFocusedElement()))
|
|
{
|
|
m_scrollLeft->Focus(::FocusState::Programmatic);
|
|
}
|
|
m_scrollRight->Visibility = scrollRightVisibility;
|
|
}
|
|
|
|
if (shouldTryFocusScrollRight && scrollRightVisibility == ::Visibility::Visible)
|
|
{
|
|
m_scrollRight->Focus(::FocusState::Programmatic);
|
|
}
|
|
|
|
if (ScrollButtonsPlacement == OverflowButtonPlacement::Above && m_expressionContent != nullptr)
|
|
{
|
|
double left = m_scrollLeft != nullptr && m_scrollLeft->Visibility == ::Visibility::Visible ? ScrollButtonsWidth : 0;
|
|
double right = m_scrollRight != nullptr && m_scrollRight->Visibility == ::Visibility::Visible ? ScrollButtonsWidth : 0;
|
|
if (m_expressionContainer->Padding.Left != left || m_expressionContainer->Padding.Right != right)
|
|
{
|
|
m_expressionContainer->ViewChanged -= m_containerViewChangedToken;
|
|
|
|
m_expressionContainer->Padding = Thickness(left, 0, right, 0);
|
|
m_expressionContent->Margin = Thickness(-left, 0, -right, 0);
|
|
m_expressionContainer->UpdateLayout();
|
|
m_expressionContainer->Measure(m_expressionContainer->RenderSize);
|
|
|
|
m_containerViewChangedToken = m_expressionContainer->ViewChanged +=
|
|
ref new EventHandler<ScrollViewerViewChangedEventArgs ^>(this, &OverflowTextBlock::OnViewChanged);
|
|
}
|
|
}
|
|
}
|
|
|
|
void OverflowTextBlock::UnregisterEventHandlers()
|
|
{
|
|
// Unregister the event handlers
|
|
if (m_scrollLeft != nullptr)
|
|
{
|
|
m_scrollLeft->Click -= m_scrollLeftClickEventToken;
|
|
}
|
|
|
|
if (m_scrollRight != nullptr)
|
|
{
|
|
m_scrollRight->Click -= m_scrollRightClickEventToken;
|
|
}
|
|
|
|
if (m_expressionContainer != nullptr)
|
|
{
|
|
m_expressionContainer->ViewChanged -= m_containerViewChangedToken;
|
|
}
|
|
}
|
|
|
|
void OverflowTextBlock::OnViewChanged(_In_opt_ Object ^ /*sender*/, _In_opt_ ScrollViewerViewChangedEventArgs ^ /*args*/)
|
|
{
|
|
UpdateScrollButtons();
|
|
}
|
|
|
|
void OverflowTextBlock::OnScrollButtonsPlacementPropertyChanged(OverflowButtonPlacement /*oldValue*/, OverflowButtonPlacement newValue)
|
|
{
|
|
if (newValue == OverflowButtonPlacement::InLine)
|
|
{
|
|
m_expressionContainer->Padding = Thickness(0);
|
|
m_expressionContent->Margin = Thickness(0);
|
|
}
|
|
UpdateScrollButtons();
|
|
}
|