Matthew Kosarek 923eac37f2
feature: implementation of tooltips in the _TestWindowingOwner and minor bugfixes to the multiple windows example app (#181510)
## What's new?
- Implemented `_TestTooltipWindowController` in the `TestWindowingOwner`
- Fixed how child windows of the main window are rendered in the
application
- Introduced `WindowManager.getChildrenOf` to simplify filtering and
rendering of the children of a parent window
- Added a test for the tooltip window
- Added a test for modal dialogs of the main window

## Pre-launch Checklist

- [X] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [X] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [X] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [X] I signed the [CLA].
- [X] I listed at least one issue that this PR fixes in the description
above.
- [X] I updated/added relevant documentation (doc comments with `///`).
- [X] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [X] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [X] All existing and new tests are passing.
2026-02-03 18:56:54 +00:00

152 lines
4.5 KiB
Dart

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: implementation_imports
import 'package:flutter/widgets.dart';
import 'package:flutter/src/widgets/_window.dart';
import 'package:flutter/src/widgets/_window_positioner.dart';
class KeyedWindow {
KeyedWindow({
this.isMainWindow = false,
required this.key,
required this.controller,
});
BaseWindowController? get parent {
switch (controller) {
case RegularWindowController():
return null;
case DialogWindowController dialogController:
return dialogController.parent;
case TooltipWindowController tooltipController:
return tooltipController.parent;
default:
throw Exception('Unknown controller type');
}
}
final bool isMainWindow;
final UniqueKey key;
final BaseWindowController controller;
}
/// Provides access to the windows created by the application.
///
/// The window manager manages a flat list of all of the [BaseWindowController]s
/// that have been created by the application as well as which controller is
/// currently selected by the UI.
class WindowManager extends ChangeNotifier {
WindowManager({required List<KeyedWindow> initialWindows})
: _windows = initialWindows;
final List<KeyedWindow> _windows;
List<KeyedWindow> get windows => _windows;
void add(KeyedWindow window) {
_windows.add(window);
notifyListeners();
}
void remove(UniqueKey key) {
_windows.removeWhere((KeyedWindow window) => window.key == key);
notifyListeners();
}
Iterable<KeyedWindow> getWindows({required BaseWindowController? parent}) {
return _windows.where((KeyedWindow window) => window.parent == parent);
}
}
/// Provides access to the [WindowManager] from the widget tree.
class WindowManagerAccessor extends InheritedNotifier<WindowManager> {
const WindowManagerAccessor({
super.key,
required super.child,
required WindowManager windowManager,
}) : super(notifier: windowManager);
static WindowManager of(BuildContext context) {
final WindowManagerAccessor? result = context
.dependOnInheritedWidgetOfExactType<WindowManagerAccessor>();
assert(result != null, 'No WindowManager found in context');
return result!.notifier!;
}
}
class TooltipSettings {}
/// Settings that control the behavior of newly created windows.
class WindowSettings {
WindowSettings({
this.regularSize = const Size(800, 600),
this.dialogSize = const Size(400, 400),
this.positioner = const WindowPositioner(
parentAnchor: WindowPositionerAnchor.right,
childAnchor: WindowPositionerAnchor.left,
),
});
/// The initial size for newly created regular windows.
Size regularSize;
/// The initial size of the dialog window.
Size dialogSize;
/// The positioner used to determine where new tooltips and popups are placed.
WindowPositioner positioner;
}
/// Provides access to the [WindowSettings] from the widget tree.
class WindowSettingsAccessor extends InheritedWidget {
const WindowSettingsAccessor({
super.key,
required super.child,
required this.windowSettings,
});
final WindowSettings windowSettings;
static WindowSettings of(BuildContext context) {
final WindowSettingsAccessor? result = context
.dependOnInheritedWidgetOfExactType<WindowSettingsAccessor>();
assert(result != null, 'No WindowSettings found in context');
return result!.windowSettings;
}
@override
bool updateShouldNotify(WindowSettingsAccessor oldWidget) {
return windowSettings != oldWidget.windowSettings;
}
}
class CallbackDialogWindowControllerDelegate
with DialogWindowControllerDelegate {
CallbackDialogWindowControllerDelegate({required this.onDestroyed});
@override
void onWindowDestroyed() {
onDestroyed();
super.onWindowDestroyed();
}
final VoidCallback onDestroyed;
}
String anchorToString(WindowPositionerAnchor anchor) {
return switch (anchor) {
WindowPositionerAnchor.center => 'Center',
WindowPositionerAnchor.top => 'Top',
WindowPositionerAnchor.bottom => 'Bottom',
WindowPositionerAnchor.left => 'Left',
WindowPositionerAnchor.right => 'Right',
WindowPositionerAnchor.topLeft => 'Top Left',
WindowPositionerAnchor.bottomLeft => 'Bottom Left',
WindowPositionerAnchor.topRight => 'Top Right',
WindowPositionerAnchor.bottomRight => 'Bottom Right',
};
}