|
| 1 | +// Copyright (c) Microsoft Corporation. |
| 2 | +// Licensed under the MIT License. |
| 3 | +#include "pch.h" |
| 4 | +#include "DebuggerUIIsland.h" |
| 5 | + |
| 6 | +#include <AutoDraw.h> |
| 7 | +#include <react/renderer/attributedstring/AttributedStringBox.h> |
| 8 | +#include <react/renderer/textlayoutmanager/WindowsTextLayoutManager.h> |
| 9 | +#include <winrt/Microsoft.UI.Composition.h> |
| 10 | +#include <winrt/Microsoft.UI.Content.h> |
| 11 | +#include <winrt/Microsoft.UI.Input.h> |
| 12 | +#include "CompositionContextHelper.h" |
| 13 | +#include "TextDrawing.h" |
| 14 | + |
| 15 | +namespace winrt::Microsoft::ReactNative::implementation { |
| 16 | + |
| 17 | +constexpr float debuggerUIFontSize = 10.0f; |
| 18 | +constexpr float debuggerTextMargin = 4.0f; |
| 19 | + |
| 20 | +DebuggerUIIsland::DebuggerUIIsland( |
| 21 | + const winrt::Microsoft::UI::Composition::Compositor &compositor, |
| 22 | + winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, |
| 23 | + winrt::Microsoft::ReactNative::Composition::Theme theme) noexcept |
| 24 | + : m_compositor(compositor), m_compContext(compContext), m_theme(theme) { |
| 25 | + m_backgroundVisual = m_compositor.CreateSpriteVisual(); |
| 26 | + m_backgroundVisual.RelativeSizeAdjustment({1.0f, 1.0f}); |
| 27 | + |
| 28 | + auto backgroundBrush = m_compositor.CreateColorBrush({100, 0, 0, 0}); |
| 29 | + m_backgroundVisual.Brush(backgroundBrush); |
| 30 | + |
| 31 | + m_TextVisual = m_compositor.CreateSpriteVisual(); |
| 32 | + m_TextVisual.IsPixelSnappingEnabled(true); |
| 33 | + |
| 34 | + m_backgroundVisual.Children().InsertAtTop(m_TextVisual); |
| 35 | +} |
| 36 | + |
| 37 | +DebuggerUIIsland::~DebuggerUIIsland() noexcept { |
| 38 | + m_island.StateChanged(m_islandStateChangedToken); |
| 39 | +} |
| 40 | + |
| 41 | +void DebuggerUIIsland::Redraw() noexcept { |
| 42 | + if (!m_island) |
| 43 | + return; |
| 44 | + |
| 45 | + if (m_island.ActualSize().x == 0 || m_island.ActualSize().y == 0) |
| 46 | + return; |
| 47 | + |
| 48 | + auto scaleFactor = m_island.Environment().DisplayScale(); |
| 49 | + |
| 50 | + auto attributedString = facebook::react::AttributedString{}; |
| 51 | + auto fragment = facebook::react::AttributedString::Fragment{}; |
| 52 | + fragment.string = m_message; |
| 53 | + fragment.textAttributes.fontSize = debuggerUIFontSize; |
| 54 | + attributedString.appendFragment(std::move(fragment)); |
| 55 | + |
| 56 | + // Resume Icon |
| 57 | + auto iconFragment = facebook::react::AttributedString::Fragment{}; |
| 58 | + iconFragment.string = " \uF08F"; |
| 59 | + iconFragment.textAttributes.fontFamily = "Segoe Fluent Icons"; |
| 60 | + iconFragment.textAttributes.fontSize = debuggerUIFontSize; |
| 61 | + attributedString.appendFragment(std::move(iconFragment)); |
| 62 | + |
| 63 | + auto attributedStringBox = facebook::react::AttributedStringBox{attributedString}; |
| 64 | + |
| 65 | + facebook::react::LayoutConstraints constraints; |
| 66 | + constraints.maximumSize.width = std::max(0.0f, m_island.ActualSize().x - debuggerTextMargin * 2 * scaleFactor); |
| 67 | + constraints.maximumSize.height = std::max(0.0f, m_island.ActualSize().y - debuggerTextMargin * 2 * scaleFactor); |
| 68 | + |
| 69 | + auto textAttributes = facebook::react::TextAttributes{}; |
| 70 | + textAttributes.foregroundColor = facebook::react::blackColor(); |
| 71 | + |
| 72 | + winrt::com_ptr<::IDWriteTextLayout> textLayout; |
| 73 | + facebook::react::WindowsTextLayoutManager::GetTextLayout(attributedStringBox, {}, constraints, textLayout); |
| 74 | + |
| 75 | + DWRITE_TEXT_METRICS tm; |
| 76 | + textLayout->GetMetrics(&tm); |
| 77 | + |
| 78 | + winrt::Windows::Foundation::Size surfaceSize = { |
| 79 | + std::ceilf(std::min(constraints.maximumSize.width, tm.width + debuggerTextMargin * 2 * scaleFactor)), |
| 80 | + std::ceilf(std::min(constraints.maximumSize.height, tm.height + debuggerTextMargin * 2 * scaleFactor))}; |
| 81 | + auto drawingSurface = m_compContext.CreateDrawingSurfaceBrush( |
| 82 | + surfaceSize, |
| 83 | + winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized, |
| 84 | + winrt::Windows::Graphics::DirectX::DirectXAlphaMode::Premultiplied); |
| 85 | + |
| 86 | + POINT offset; |
| 87 | + { |
| 88 | + ::Microsoft::ReactNative::Composition::AutoDrawDrawingSurface autoDraw(drawingSurface, scaleFactor, &offset); |
| 89 | + if (auto d2dDeviceContext = autoDraw.GetRenderTarget()) { |
| 90 | + d2dDeviceContext->Clear(D2D1::ColorF{1.0f, 1.0f, 0.76f, 1.0f}); |
| 91 | + |
| 92 | + auto theme = winrt::get_self<winrt::Microsoft::ReactNative::Composition::implementation::Theme>(m_theme); |
| 93 | + |
| 94 | + Composition::RenderText( |
| 95 | + *d2dDeviceContext, |
| 96 | + *textLayout, |
| 97 | + attributedStringBox.getValue(), |
| 98 | + textAttributes, |
| 99 | + {static_cast<float>(offset.x + std::floorf(debuggerTextMargin * scaleFactor)), |
| 100 | + static_cast<float>(offset.y + std::floorf(debuggerTextMargin * scaleFactor))}, |
| 101 | + scaleFactor, |
| 102 | + *theme); |
| 103 | + } |
| 104 | + |
| 105 | + drawingSurface.HorizontalAlignmentRatio(0.0f); |
| 106 | + drawingSurface.Stretch(winrt::Microsoft::ReactNative::Composition::Experimental::CompositionStretch::None); |
| 107 | + |
| 108 | + m_TextVisual.Brush(winrt::Microsoft::ReactNative::Composition::Experimental::implementation:: |
| 109 | + MicrosoftCompositionContextHelper::InnerBrush(drawingSurface)); |
| 110 | + m_TextVisual.Size({surfaceSize.Width, surfaceSize.Height}); |
| 111 | + |
| 112 | + m_debuggerHitRect = { |
| 113 | + m_island.ActualSize().x / 2 - tm.width / 2 + debuggerTextMargin * scaleFactor, |
| 114 | + debuggerTextMargin * scaleFactor, |
| 115 | + surfaceSize.Width, |
| 116 | + surfaceSize.Height}; |
| 117 | + |
| 118 | + m_TextVisual.Offset({m_debuggerHitRect.X, m_debuggerHitRect.Y, 0.0f}); |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +void DebuggerUIIsland::Message(std::string &&value) noexcept { |
| 123 | + m_message = value; |
| 124 | + Redraw(); |
| 125 | +} |
| 126 | + |
| 127 | +winrt::Microsoft::UI::Content::ContentIsland DebuggerUIIsland::Island() noexcept { |
| 128 | + if (!m_island) { |
| 129 | + m_island = winrt::Microsoft::UI::Content::ContentIsland::Create(m_backgroundVisual); |
| 130 | + |
| 131 | + m_islandStateChangedToken = |
| 132 | + m_island.StateChanged([weakThis = weak_from_this()]( |
| 133 | + winrt::Microsoft::UI::Content::ContentIsland const &island, |
| 134 | + winrt::Microsoft::UI::Content::ContentIslandStateChangedEventArgs const &args) { |
| 135 | + if (auto pThis = weakThis.lock()) { |
| 136 | + if (args.DidRasterizationScaleChange() || args.DidActualSizeChange()) { |
| 137 | + pThis->Redraw(); |
| 138 | + } |
| 139 | + } |
| 140 | + }); |
| 141 | + |
| 142 | + auto pointerSource = winrt::Microsoft::UI::Input::InputPointerSource::GetForIsland(m_island); |
| 143 | + |
| 144 | + m_islandPointerUpToken = |
| 145 | + pointerSource.PointerReleased([weakThis = weak_from_this()]( |
| 146 | + winrt::Microsoft::UI::Input::InputPointerSource const &, |
| 147 | + winrt::Microsoft::UI::Input::PointerEventArgs const &args) { |
| 148 | + if (auto pThis = weakThis.lock()) { |
| 149 | + auto position = args.CurrentPoint().Position(); |
| 150 | + if (position.X >= pThis->m_debuggerHitRect.X && position.Y >= pThis->m_debuggerHitRect.Y && |
| 151 | + position.X <= pThis->m_debuggerHitRect.X + pThis->m_debuggerHitRect.Width && |
| 152 | + position.Y <= pThis->m_debuggerHitRect.Y + pThis->m_debuggerHitRect.Height) { |
| 153 | + pThis->m_resumedEvent(nullptr, nullptr); |
| 154 | + } |
| 155 | + } |
| 156 | + }); |
| 157 | + } |
| 158 | + return m_island; |
| 159 | +} |
| 160 | + |
| 161 | +winrt::event_token DebuggerUIIsland::Resumed( |
| 162 | + winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable> const &handler) noexcept { |
| 163 | + return m_resumedEvent.add(handler); |
| 164 | +} |
| 165 | +void DebuggerUIIsland::Resumed(winrt::event_token const &token) noexcept { |
| 166 | + m_resumedEvent.remove(token); |
| 167 | +} |
| 168 | + |
| 169 | +} // namespace winrt::Microsoft::ReactNative::implementation |
0 commit comments