Catch-all commit for outstanding pre-existing local changes. Mixes several themes that would normally be split: - Rename: UxPlugin → XPlugin across iOS, macOS, Android registrants. - New top-level packages under lib/src/: anim/ (animated values, panes, sheets, dock, measured), core/ (Emitter, ReactiveBuilder scaffolding, presenter/widget/value/dispose primitives), navi/ (Screen/ScreenStack/Router/hero/transitions), reactive/. - Edits across existing plugins (clipboard, crash, file, gallery, keyboard, scanner, sensor, url) to align with the new core. - Test updates and CHANGELOG/README touches accompanying the above.
385 lines
9.4 KiB
Dart
385 lines
9.4 KiB
Dart
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:ux/src/core/core.dart';
|
|
|
|
/// WIP
|
|
abstract class Pane with Emitter {
|
|
Pane();
|
|
|
|
final Map<Object?, Widget> widgets = {};
|
|
final Map<Object?, Element> elements = {};
|
|
final Map<Object?, RenderBox> renderBoxes = {};
|
|
|
|
// ready to manage children
|
|
PaneElement? element;
|
|
PaneRender? renderer;
|
|
|
|
bool get needsCompositing => renderer?.needsCompositing ?? false;
|
|
|
|
Size get size => renderer!.size;
|
|
|
|
BoxConstraints get constraints => renderer!.constraints;
|
|
|
|
void markNeedsLayout() => renderer?.markNeedsLayout();
|
|
|
|
void markNeedsPaint() => renderer?.markNeedsPaint();
|
|
|
|
void attachPipeline(PipelineOwner owner) {
|
|
for (final child in renderBoxes.values) {
|
|
if (child.owner == null) {
|
|
child.attach(owner);
|
|
}
|
|
}
|
|
}
|
|
|
|
void detachPipeline() {
|
|
for (final child in renderBoxes.values) {
|
|
child.detach();
|
|
}
|
|
}
|
|
|
|
void visitChildrenRender(RenderObjectVisitor visitor) =>
|
|
renderBoxes.values.forEach(visitor);
|
|
|
|
void setupParentData(covariant RenderObject child, covariant Object? slot);
|
|
|
|
List<DiagnosticsNode> debugDescribeChildren() => const [];
|
|
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {}
|
|
|
|
void applyPaintTransform(covariant RenderObject child, Matrix4 transform) {}
|
|
|
|
bool hitTestChildren(BoxHitTestResult result, Offset position) => false;
|
|
|
|
bool hitTestSelf(Offset position) => false;
|
|
|
|
/// [constraints] are available
|
|
/// [size] must be set
|
|
void performLayout();
|
|
|
|
void paint(PaintingContext context, Offset offset);
|
|
|
|
void insertRenderObjectChild(
|
|
PaneElement paneElement, covariant RenderBox child, Object? slot) {
|
|
setupParentData(child, slot);
|
|
renderBoxes[slot] = child;
|
|
renderer?.adoptChild(child);
|
|
}
|
|
|
|
void moveRenderObjectChild(PaneElement paneElement, RenderObject child,
|
|
Object? oldSlot, Object? newSlot) {}
|
|
|
|
void removeRenderObjectChild(
|
|
PaneElement paneElement, RenderObject child, Object? slot) {
|
|
renderBoxes.remove(slot);
|
|
renderer?.dropChild(child);
|
|
}
|
|
|
|
void visitChildrenElement(ElementVisitor visitor) =>
|
|
elements.values.forEach(visitor);
|
|
|
|
Widget itemBuilder(BuildContext context, Object? slot);
|
|
|
|
RenderBox? invokeLayoutUpsert(Object? slot) {
|
|
if (element == null) return null;
|
|
RenderBox? child = renderBoxes[slot];
|
|
if (child != null) return child;
|
|
renderer?.invokeLayoutCallback((constraints) {
|
|
child = buildChild(slot);
|
|
});
|
|
return child;
|
|
}
|
|
|
|
void invokeLayoutRemove(Object? slot) {
|
|
if (element == null) return;
|
|
renderer?.invokeLayoutCallback((constraints) {
|
|
removeSlotElement(slot);
|
|
});
|
|
}
|
|
|
|
void removeSlotElement(Object? slot) {
|
|
final element = this.element;
|
|
if (element == null) return;
|
|
elements.remove(slot)?.pipe(element.deactivateChild);
|
|
widgets.remove(slot);
|
|
}
|
|
|
|
void mountElement(Element? parent, Object? newSlot, PaneElement element) {
|
|
this.element = element;
|
|
}
|
|
|
|
void unmountElement() {
|
|
for (final child in renderBoxes.values) {
|
|
renderer?.dropChild(child);
|
|
}
|
|
renderBoxes.clear();
|
|
elements.clear();
|
|
widgets.clear();
|
|
|
|
element = null;
|
|
}
|
|
|
|
RenderBox? buildChild(Object? slot) {
|
|
final element = this.element;
|
|
if (element == null) return null;
|
|
|
|
element.owner!.buildScope(element, () {
|
|
Widget newWidget;
|
|
try {
|
|
newWidget = itemBuilder(element, slot);
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
|
|
final oldElement = elements[slot];
|
|
final oldWidget = widgets[slot];
|
|
if (oldElement == null) {
|
|
final newElement = element.inflateWidget(newWidget, slot);
|
|
elements[slot] = newElement;
|
|
widgets[slot] = newWidget;
|
|
} else if (oldWidget != null && Widget.canUpdate(oldWidget, newWidget)) {
|
|
oldElement.update(newWidget);
|
|
widgets[slot] = newWidget;
|
|
} else {
|
|
element.deactivateChild(oldElement);
|
|
final newElement = element.inflateWidget(newWidget, slot);
|
|
elements[slot] = newElement;
|
|
widgets[slot] = newWidget;
|
|
}
|
|
});
|
|
return elements[slot]?.renderObject as RenderBox?;
|
|
}
|
|
|
|
void forgetChildElement(Element child) {
|
|
elements.removeWhere((_, value) => value == child);
|
|
}
|
|
}
|
|
|
|
class PaneView extends StatelessWidget {
|
|
const PaneView({
|
|
required this.controller,
|
|
required this.scrollController,
|
|
this.scrollPhysics,
|
|
super.key,
|
|
});
|
|
|
|
final Pane controller;
|
|
final ScrollController scrollController;
|
|
final ScrollPhysics? scrollPhysics;
|
|
|
|
Widget buildViewport(BuildContext context, ViewportOffset offset) {
|
|
return PaneViewport(controller: controller);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scrollable(
|
|
controller: scrollController,
|
|
physics: scrollPhysics,
|
|
viewportBuilder: buildViewport,
|
|
);
|
|
}
|
|
}
|
|
|
|
class PaneViewport extends RenderObjectWidget {
|
|
const PaneViewport({
|
|
required this.controller,
|
|
super.key,
|
|
});
|
|
|
|
final Pane controller;
|
|
|
|
@override
|
|
RenderObjectElement createElement() {
|
|
return PaneElement(this, controller);
|
|
}
|
|
|
|
@override
|
|
RenderObject createRenderObject(BuildContext context) {
|
|
return PaneRender(controller: controller);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(
|
|
BuildContext context, covariant PaneRender renderObject) {
|
|
renderObject.controller = controller;
|
|
}
|
|
}
|
|
|
|
class PaneElement extends RenderObjectElement {
|
|
PaneElement(super.widget, this._controller);
|
|
|
|
Pane _controller;
|
|
|
|
Pane get controller => _controller;
|
|
|
|
@override
|
|
void insertRenderObjectChild(
|
|
covariant RenderBox child, covariant Object? slot) {
|
|
controller.insertRenderObjectChild(this, child, slot);
|
|
}
|
|
|
|
@override
|
|
void moveRenderObjectChild(covariant RenderObject child,
|
|
covariant Object? oldSlot, covariant Object? newSlot) {
|
|
controller.moveRenderObjectChild(this, child, oldSlot, newSlot);
|
|
}
|
|
|
|
@override
|
|
void removeRenderObjectChild(
|
|
covariant RenderObject child, covariant Object? slot) {
|
|
controller.removeRenderObjectChild(this, child, slot);
|
|
}
|
|
|
|
@override
|
|
void visitChildren(ElementVisitor visitor) =>
|
|
controller.visitChildrenElement(visitor);
|
|
|
|
@override
|
|
void deactivateChild(Element child) => super.deactivateChild(child);
|
|
|
|
@override
|
|
void mount(Element? parent, Object? newSlot) {
|
|
super.mount(parent, newSlot);
|
|
controller.mountElement(parent, newSlot, this);
|
|
}
|
|
|
|
@override
|
|
void unmount() {
|
|
controller.unmountElement();
|
|
super.unmount();
|
|
}
|
|
|
|
@override
|
|
void update(covariant PaneViewport newWidget) {
|
|
final oldController = _controller;
|
|
final newController = newWidget.controller;
|
|
if (oldController != newController) {
|
|
oldController.unmountElement();
|
|
_controller = newController;
|
|
newController.mountElement(null, null, this);
|
|
}
|
|
super.update(newWidget);
|
|
}
|
|
|
|
@override
|
|
void forgetChild(Element child) {
|
|
controller.forgetChildElement(child);
|
|
super.forgetChild(child);
|
|
}
|
|
|
|
@override
|
|
Element inflateWidget(Widget newWidget, Object? newSlot) =>
|
|
super.inflateWidget(newWidget, newSlot);
|
|
|
|
@override
|
|
PaneRender get renderObject => super.renderObject as PaneRender;
|
|
|
|
@override
|
|
PaneViewport get widget => super.widget as PaneViewport;
|
|
}
|
|
|
|
class PaneRender extends RenderBox {
|
|
PaneRender({
|
|
required Pane controller,
|
|
}) : _controller = controller;
|
|
|
|
Pane _controller;
|
|
|
|
Pane get controller => _controller;
|
|
|
|
set controller(Pane value) {
|
|
if (_controller == value) return;
|
|
if (attached) {
|
|
_controller.detachPipeline();
|
|
_controller.renderer = null;
|
|
}
|
|
_controller = value;
|
|
if (attached) {
|
|
_controller.attachPipeline(owner!);
|
|
_controller.renderer = this;
|
|
markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void attach(PipelineOwner owner) {
|
|
super.attach(owner);
|
|
_controller.attachPipeline(owner);
|
|
_controller.renderer = this;
|
|
}
|
|
|
|
@override
|
|
void detach() {
|
|
super.detach();
|
|
_controller.detachPipeline();
|
|
_controller.renderer = null;
|
|
}
|
|
|
|
@override
|
|
void adoptChild(RenderObject child) => super.adoptChild(child);
|
|
|
|
@override
|
|
void dropChild(RenderObject child) => super.dropChild(child);
|
|
|
|
@override
|
|
void setupParentData(covariant RenderObject child) {
|
|
//no-op
|
|
}
|
|
|
|
@override
|
|
bool get sizedByParent => true;
|
|
|
|
@override
|
|
void performResize() {
|
|
size = constraints.biggest;
|
|
}
|
|
|
|
@override
|
|
void performLayout() {
|
|
if (controller.renderer == null) return;
|
|
controller.performLayout();
|
|
}
|
|
|
|
@override
|
|
void paint(PaintingContext context, Offset offset) {
|
|
if (controller.renderer == null) return;
|
|
controller.paint(context, offset);
|
|
}
|
|
|
|
@override
|
|
bool hitTestSelf(Offset position) => controller.hitTestSelf(position);
|
|
|
|
@override
|
|
void invokeLayoutCallback<T extends Constraints>(
|
|
LayoutCallback<T> callback) =>
|
|
super.invokeLayoutCallback(callback);
|
|
|
|
@override
|
|
bool hitTestChildren(
|
|
BoxHitTestResult result, {
|
|
required Offset position,
|
|
}) =>
|
|
controller.hitTestChildren(result, position);
|
|
|
|
@override
|
|
void visitChildren(RenderObjectVisitor visitor) =>
|
|
controller.visitChildrenRender(visitor);
|
|
|
|
@override
|
|
void applyPaintTransform(RenderObject child, Matrix4 transform) {
|
|
assert(child.parent == this);
|
|
controller.applyPaintTransform(child, transform);
|
|
}
|
|
|
|
@override
|
|
List<DiagnosticsNode> debugDescribeChildren() =>
|
|
controller.debugDescribeChildren();
|
|
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
controller.debugFillProperties(properties);
|
|
}
|
|
}
|