diff --git a/frameworks/bridge/declarative_frontend/jsview/js_scroll.cpp b/frameworks/bridge/declarative_frontend/jsview/js_scroll.cpp index 47beddaf86830de7918ae0c014ad7193f6cc2747..0abdc8711c1ecacfea3301998514fe328569521f 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_scroll.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_scroll.cpp @@ -19,13 +19,20 @@ #include "bridge/declarative_frontend/jsview/js_view_common_def.h" #include "bridge/declarative_frontend/view_stack_processor.h" #include "core/components/scroll/scroll_component.h" +#include "core/components_ng/pattern/scroll/scroll_view.h" namespace OHOS::Ace::Framework { namespace { const std::vector DISPLAY_MODE = {DisplayMode::OFF, DisplayMode::AUTO, DisplayMode::ON}; + const std::vector AXIS = { Axis::VERTICAL, Axis::HORIZONTAL, Axis::FREE, Axis::NONE }; } void JSScroll::Create(const JSCallbackInfo& info) { + if (Container::IsCurrentUseNewPipeline()) { + NG::ScrollView::Create(); + return; + } + RefPtr child; auto scrollComponent = AceType::MakeRefPtr(child); ViewStackProcessor::GetInstance()->ClaimElementId(scrollComponent); @@ -59,14 +66,20 @@ void JSScroll::Create(const JSCallbackInfo& info) void JSScroll::SetScrollable(int32_t value) { - if (value >= 0 && value < 4) { // Number of scrolling methods - auto component = ViewStackProcessor::GetInstance()->GetMainComponent(); - auto scrollComponent = AceType::DynamicCast(component); - if (scrollComponent) { - scrollComponent->SetAxisDirection((Axis)value); - } - } else { - LOGE("invalid value for SetScrollable"); + if (value < 0 || value >= static_cast(AXIS.size())) { + LOGE("value is not valid: %{public}d", value); + return; + } + + if (Container::IsCurrentUseNewPipeline()) { + NG::ScrollView::SetAxis(AXIS[value]); + return; + } + + auto component = ViewStackProcessor::GetInstance()->GetMainComponent(); + auto scrollComponent = AceType::DynamicCast(component); + if (scrollComponent) { + scrollComponent->SetAxisDirection(AXIS[value]); } } diff --git a/frameworks/core/components_ng/pattern/BUILD.gn b/frameworks/core/components_ng/pattern/BUILD.gn index 5229c07eba239e41b8cf9edb6e4b27662de1f640..3d029374045fc608b5a48cedd3c955e372cf7e25 100644 --- a/frameworks/core/components_ng/pattern/BUILD.gn +++ b/frameworks/core/components_ng/pattern/BUILD.gn @@ -37,6 +37,9 @@ build_component_ng("pattern_ng") { "list/list_pattern.cpp", "list/list_view.cpp", "page/page_pattern.cpp", + "scroll/scroll_layout_algorithm.cpp", + "scroll/scroll_pattern.cpp", + "scroll/scroll_view.cpp", "stack/stack_view.cpp", "stage/stage_manager.cpp", "swiper/swiper_layout_algorithm.cpp", diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..84c0809a4e5c9f67c13864ce424b3abe81c12918 --- /dev/null +++ b/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core/components_ng/pattern/scroll/scroll_layout_algorithm.h" + +#include + +#include "base/geometry/axis.h" +#include "base/geometry/ng/offset_t.h" +#include "base/geometry/ng/size_t.h" +#include "base/log/ace_trace.h" +#include "base/utils/utils.h" +#include "core/components_ng/pattern/scroll/scroll_layout_property.h" +#include "core/components_ng/property/layout_constraint.h" +#include "core/components_ng/property/measure_property.h" +#include "core/components_ng/property/measure_utils.h" + +namespace OHOS::Ace::NG { +namespace { + +void UpdateChildConstraint(Axis axis, const SizeF& selfIdealSize, LayoutConstraintF& contentConstraint) +{ + contentConstraint.parentIdealSize = OptionalSizeF(selfIdealSize); + if (axis == Axis::VERTICAL) { + contentConstraint.maxSize.SetHeight(Infinity()); + } else { + contentConstraint.maxSize.SetWidth(Infinity()); + } +} + +} // namespace + +void ScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) +{ + auto layoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); + CHECK_NULL_VOID(layoutProperty); + + auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL); + auto constraint = layoutProperty->GetLayoutConstraint(); + auto idealSize = CreateIdealSize(constraint.value(), axis, layoutProperty->GetMeasureType(), true); + if (GreatOrEqual(GetMainAxisSize(idealSize, axis), Infinity())) { + LOGE("the scroll is infinity, error"); + return; + } + + // Calculate child layout constraint. + auto padding = layoutProperty->CreatePaddingPropertyF(); + auto childLayoutConstraint = layoutProperty->CreateChildConstraint(); + UpdateChildConstraint(axis, idealSize - padding.Size(), childLayoutConstraint); + + // Measure child. + auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(0); + if (!childWrapper) { + LOGI("There is no child."); + return; + } + childWrapper->Measure(childLayoutConstraint); + + // Use child size when self idea size of scroll is not setted. + auto childSize = childWrapper->GetGeometryNode()->GetFrameSize(); + if (!constraint->selfIdealSize.Width().has_value()) { + idealSize.SetWidth(childSize.Width()); + } + if (!constraint->selfIdealSize.Height().has_value()) { + idealSize.SetHeight(childSize.Height()); + } + + auto geometryNode = layoutWrapper->GetGeometryNode(); + CHECK_NULL_VOID(geometryNode); + geometryNode->SetFrameSize(idealSize); +} + +void ScrollLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) +{ + CHECK_NULL_VOID(layoutWrapper); + auto layoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); + CHECK_NULL_VOID(layoutProperty); + auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL); + auto geometryNode = layoutWrapper->GetGeometryNode(); + CHECK_NULL_VOID(geometryNode); + auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(0); + CHECK_NULL_VOID(childWrapper); + auto childGeometryNode = childWrapper->GetGeometryNode(); + CHECK_NULL_VOID(childGeometryNode); + + auto parentOffset = + layoutWrapper->GetGeometryNode()->GetParentGlobalOffset() + layoutWrapper->GetGeometryNode()->GetFrameOffset(); + auto size = geometryNode->GetFrameSize(); + auto padding = layoutProperty->CreatePaddingPropertyF(); + MinusPaddingToSize(padding, size); + auto childSize = childGeometryNode->GetFrameSize(); + auto scrollableDistance = GetMainAxisSize(childSize, axis) - GetMainAxisSize(size, axis); + currentOffset_ = std::clamp(currentOffset_, -scrollableDistance, 0.0f); + auto currentOffset = axis == Axis::VERTICAL ? OffsetF(0.0f, currentOffset_) : OffsetF(currentOffset_, 0.0f); + childGeometryNode->SetFrameOffset(padding.Offset() + currentOffset); + childWrapper->Layout(parentOffset); +} + +} // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.h b/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.h new file mode 100644 index 0000000000000000000000000000000000000000..ad9f7340369f1eb4d0eb12814cf88086b0bfba41 --- /dev/null +++ b/frameworks/core/components_ng/pattern/scroll/scroll_layout_algorithm.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_SCROLL_SCROLL_LAYOUT_ALGORITHM_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_SCROLL_SCROLL_LAYOUT_ALGORITHM_H + +#include + +#include "base/geometry/axis.h" +#include "base/memory/referenced.h" +#include "core/components_ng/layout/layout_algorithm.h" +#include "core/components_ng/layout/layout_wrapper.h" +#include "core/components/scroll/scroll_component.h" + +namespace OHOS::Ace::NG { + +class ACE_EXPORT ScrollLayoutAlgorithm : public LayoutAlgorithm { + DECLARE_ACE_TYPE(ScrollLayoutAlgorithm, LayoutAlgorithm); + +public: + explicit ScrollLayoutAlgorithm(float currentOffset) : currentOffset_(currentOffset) {} + ~ScrollLayoutAlgorithm() override = default; + + void OnReset() override {} + + void SetCurrentOffset(float offset) + { + currentOffset_ = offset; + } + + float GetCurrentOffset() const + { + return currentOffset_; + } + + void Measure(LayoutWrapper* layoutWrapper) override; + + void Layout(LayoutWrapper* layoutWrapper) override; + +private: + float currentOffset_ = 0.0f; +}; + +} // namespace OHOS::Ace::NG + +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_SCROLL_SCROLL_LAYOUT_ALGORITHM_H diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_layout_property.h b/frameworks/core/components_ng/pattern/scroll/scroll_layout_property.h new file mode 100644 index 0000000000000000000000000000000000000000..7e96fece4a8e2e2a09f3b7a501a83a0ce7585589 --- /dev/null +++ b/frameworks/core/components_ng/pattern/scroll/scroll_layout_property.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_SCROLL_SCROLL_LAYOUT_PROPERTY_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_SCROLL_SCROLL_LAYOUT_PROPERTY_H + +#include "base/geometry/axis.h" +#include "base/utils/macros.h" +#include "core/components/common/layout/constants.h" +#include "core/components_ng/layout/layout_property.h" +#include "core/components_ng/property/property.h" + +namespace OHOS::Ace::NG { +class ACE_EXPORT ScrollLayoutProperty : public LayoutProperty { + DECLARE_ACE_TYPE(ScrollLayoutProperty, LayoutProperty); + +public: + ScrollLayoutProperty() = default; + ~ScrollLayoutProperty() override = default; + + RefPtr Clone() const override + { + auto value = MakeRefPtr(); + value->LayoutProperty::UpdateLayoutProperty(DynamicCast(this)); + value->propAxis_ = CloneAxis(); + return value; + } + + void Reset() override + { + LayoutProperty::Reset(); + ResetAxis(); + } + + ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(Axis, Axis, PROPERTY_UPDATE_MEASURE); +}; + +} // namespace OHOS::Ace::NG + +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_SCROLL_SCROLL_LAYOUT_PROPERTY_H diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca3db03c3790cd9ef2636c8f39e0917ce8540167 --- /dev/null +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core/components_ng/pattern/scroll/scroll_pattern.h" + +#include "base/geometry/axis.h" +#include "base/utils/utils.h" +#include "core/components/scroll/scrollable.h" +#include "core/components_ng/property/measure_utils.h" +#include "core/components_ng/property/property.h" +#include "core/components_ng/pattern/scroll/scroll_layout_algorithm.h" +#include "core/components_ng/pattern/scroll/scroll_layout_property.h" + +namespace OHOS::Ace::NG { + +void ScrollPattern::OnAttachToFrameNode() +{ + auto host = GetHost(); + CHECK_NULL_VOID(host); + host->GetRenderContext()->SetClipToFrame(true); +} + +void ScrollPattern::OnModifyDone() +{ + auto host = GetHost(); + CHECK_NULL_VOID(host); + auto layoutProperty = host->GetLayoutProperty(); + CHECK_NULL_VOID(layoutProperty); + + auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL); + if (axis_ == axis && scrollableEvent_) { + LOGD("Direction not changed, need't resister scroll event again."); + return; + } + + axis_ = axis; + auto task = [weak = WeakClaim(this)](double offset, int32_t source) { + if (source != SCROLL_FROM_START) { + auto pattern = weak.Upgrade(); + if (pattern) { + pattern->UpdateCurrentOffset(static_cast(offset)); + } + } + return true; + }; + + auto hub = host->GetEventHub(); + CHECK_NULL_VOID(hub); + auto gestureHub = hub->GetOrCreateGestureEventHub(); + CHECK_NULL_VOID(gestureHub); + if (scrollableEvent_) { + gestureHub->RemoveScrollableEvent(scrollableEvent_); + } + scrollableEvent_ = MakeRefPtr(axis); + scrollableEvent_->SetScrollPositionCallback(std::move(task)); + gestureHub->AddScrollableEvent(scrollableEvent_); +} + +bool ScrollPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dirty, bool skipMeasure, bool skipLayout) +{ + if (skipMeasure && skipLayout) { + return false; + } + auto layoutAlgorithmWrapper = DynamicCast(dirty->GetLayoutAlgorithm()); + CHECK_NULL_RETURN(layoutAlgorithmWrapper, false); + auto layoutAlgorithm = DynamicCast(layoutAlgorithmWrapper->GetLayoutAlgorithm()); + CHECK_NULL_RETURN(layoutAlgorithm, false); + currentOffset_ = layoutAlgorithm->GetCurrentOffset(); + return false; +} + +void ScrollPattern::UpdateCurrentOffset(float offset) +{ + auto host = GetHost(); + CHECK_NULL_VOID(host); + currentOffset_ = currentOffset_ + offset; + host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT); +} + +} // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h new file mode 100644 index 0000000000000000000000000000000000000000..c985e6c5f8504f1c10a5ba5b7ecc7148fe7e7adf --- /dev/null +++ b/frameworks/core/components_ng/pattern/scroll/scroll_pattern.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H + +#include "core/components_ng/event/event_hub.h" +#include "core/components_ng/pattern/pattern.h" +#include "core/components_ng/pattern/scroll/scroll_layout_algorithm.h" +#include "core/components_ng/pattern/scroll/scroll_layout_property.h" + +namespace OHOS::Ace::NG { + +class ScrollPattern : public Pattern { + DECLARE_ACE_TYPE(ScrollPattern, Pattern); + +public: + ScrollPattern() = default; + ~ScrollPattern() override = default; + + bool IsAtomicNode() const override + { + return false; + } + + RefPtr CreateLayoutProperty() override + { + return MakeRefPtr(); + } + + RefPtr CreateLayoutAlgorithm() override + { + auto layoutAlgorithm = MakeRefPtr(currentOffset_); + return layoutAlgorithm; + } + + void UpdateCurrentOffset(float offset); + +private: + void OnModifyDone() override; + void OnAttachToFrameNode() override; + bool OnDirtyLayoutWrapperSwap(const RefPtr& dirty, bool skipMeasure, bool skipLayout) override; + + RefPtr scrollableEvent_; + Axis axis_ = Axis::VERTICAL; + float currentOffset_ = 0.0f; +}; + +} // namespace OHOS::Ace::NG + +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_PATTERN_H diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_view.cpp b/frameworks/core/components_ng/pattern/scroll/scroll_view.cpp new file mode 100644 index 0000000000000000000000000000000000000000..996b8a7c835b1304fa29951d13d0979160a2f0bd --- /dev/null +++ b/frameworks/core/components_ng/pattern/scroll/scroll_view.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core/components_ng/pattern/scroll/scroll_view.h" + +#include "core/components_ng/base/frame_node.h" +#include "core/components_ng/base/view_stack_processor.h" +#include "core/components_ng/pattern/scroll/scroll_pattern.h" +#include "core/components_v2/inspector/inspector_constants.h" + +namespace OHOS::Ace::NG { + +void ScrollView::Create() +{ + auto* stack = ViewStackProcessor::GetInstance(); + auto nodeId = stack->ClaimNodeId(); + auto frameNode = FrameNode::GetOrCreateFrameNode(V2::SCROLL_ETS_TAG, nodeId, + []() { return AceType::MakeRefPtr(); }); + stack->Push(frameNode); +} + +void ScrollView::SetAxis(Axis axis) +{ + ACE_UPDATE_LAYOUT_PROPERTY(ScrollLayoutProperty, Axis, axis); +} + +} // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/scroll/scroll_view.h b/frameworks/core/components_ng/pattern/scroll/scroll_view.h new file mode 100644 index 0000000000000000000000000000000000000000..8e6fda67691926f9a79fcb07a8f7164f6d6fc9c6 --- /dev/null +++ b/frameworks/core/components_ng/pattern/scroll/scroll_view.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_VIEW_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_VIEW_H + +#include "base/geometry/axis.h" +#include "base/utils/macros.h" +#include "core/components/common/layout/constants.h" + +namespace OHOS::Ace::NG { + +class ACE_EXPORT ScrollView { +public: + static void Create(); + static void SetAxis(Axis axis); +}; + +} // namespace OHOS::Ace::NG + +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_SCROLL_SCROLL_VIEW_H diff --git a/frameworks/core/components_ng/property/measure_property.h b/frameworks/core/components_ng/property/measure_property.h index b7a6398fd803d8d15010316a8676836c346c1826..43204825dfd8f081d62d53aca6c38fdcce381b40 100644 --- a/frameworks/core/components_ng/property/measure_property.h +++ b/frameworks/core/components_ng/property/measure_property.h @@ -24,6 +24,7 @@ #include #include +#include "base/geometry/ng/offset_t.h" #include "base/utils/utils.h" #include "core/components_ng/property/calc_length.h" @@ -276,6 +277,26 @@ struct PaddingPropertyT { str.append("bottom: [").append(bottom.has_value() ? std::to_string(bottom.value()) : "NA").append("]"); return str; } + + float Width() const + { + return left.value_or(0.0f) + right.value_or(0.0f); + } + + float Height() const + { + return top.value_or(0.0f) + bottom.value_or(0.0f); + } + + SizeF Size() const + { + return SizeF(Width(), Height()); + } + + OffsetF Offset() const + { + return OffsetF(left.value_or(0.0f), top.value_or(0.0f)); + } }; using PaddingProperty = PaddingPropertyT;