Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-scrollable ResizablePane #420

Merged
merged 25 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
728bfae
add `ResizablePane.noScrollBar` constructor
stMerlHin Apr 27, 2023
6963fed
update test file
stMerlHin Apr 27, 2023
4ac878e
include ResizablePane.noScrollBar in buttons_page example file
stMerlHin Apr 27, 2023
40cfee7
incremented the package version as appropriate and updated `CHANGELOG…
stMerlHin Apr 27, 2023
284c6a0
Merge branch 'dev' into non-scrollable-resizable-pane
stMerlHin May 19, 2023
9faa2b5
Update lib/src/layout/resizable_pane.dart
stMerlHin Jul 11, 2023
456f849
Update example/lib/pages/buttons_page.dart
stMerlHin Jul 11, 2023
4997362
Update lib/src/layout/resizable_pane.dart
stMerlHin Jul 11, 2023
9b4cc8b
Update CHANGELOG.md
stMerlHin Jul 11, 2023
7721946
Update lib/src/layout/resizable_pane.dart
stMerlHin Jul 11, 2023
321020d
Merge pull request #2 from macosui/dev
stMerlHin Jul 11, 2023
96c2007
Update resizable pane widget.
stMerlHin Jul 11, 2023
2a163f1
Update buttons_page.dart file to follow dart code metrics
stMerlHin Jul 11, 2023
5453636
update CHANGELOG.md file
stMerlHin Jul 11, 2023
8b3c794
run `dart format` on project
stMerlHin Jul 11, 2023
ef98be5
update docs
stMerlHin Jul 11, 2023
b46ad8e
Update resizable pane constructors docs
stMerlHin Jul 11, 2023
e71f506
update CHANGELOG.md file
stMerlHin Jul 11, 2023
063b359
Apply suggestions from code review
GroovinChip Jul 11, 2023
93e8828
Update right ResizablePanePage to be non-scrollable
stMerlHin Jul 11, 2023
e95df60
Update right ResizablePanePage to be non-scrollable
stMerlHin Jul 11, 2023
fcbcb4b
Merge branch 'macosui:dev' into dev
stMerlHin Jul 11, 2023
db27c0d
update CHANGELOG.md file
stMerlHin Jul 11, 2023
82801d2
update pubspec.yaml file
stMerlHin Jul 11, 2023
5efa234
run pub get
stMerlHin Jul 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
##[2.0.0-beta.2]
* Provide `ReziablePane.noScrollBar` constructor which in opposite to the default `ResizablePane` constructor does not have a built in `MaosScrollbar`
* The default `ResizablePane` constructor is not `const` constructor anymore
stMerlHin marked this conversation as resolved.
Show resolved Hide resolved

## [2.0.0-beta.1]
🚨 Breaking Changes 🚨
* Migrate macos_ui to [macos_window_utils](https://pub.dev/packages/macos_window_utils), which provides the following benefits:
Expand Down
18 changes: 8 additions & 10 deletions example/lib/pages/buttons_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,14 @@ class _ButtonsPageState extends State<ButtonsPage> {
],
),
children: [
ResizablePane(
minSize: 180,
startSize: 200,
windowBreakpoint: 700,
resizableSide: ResizableSide.right,
builder: (_, __) {
return const Center(
child: Text('Resizable Pane'),
);
},
ResizablePane.noScrollBar(
minSize: 180,
startSize: 200,
windowBreakpoint: 700,
resizableSide: ResizableSide.right,
child: const Center(
child: Text('Resizable Pane')
)
GroovinChip marked this conversation as resolved.
Show resolved Hide resolved
),
ContentArea(
builder: (context, scrollController) {
Expand Down
48 changes: 41 additions & 7 deletions lib/src/layout/resizable_pane.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ enum ResizableSide {
/// The [startSize] is the initial width or height depending on the orientation of the pane.
/// {@endtemplate}
class ResizablePane extends StatefulWidget {

///Create a [ResizablePane] with an internal [MacosScrollbar]
stMerlHin marked this conversation as resolved.
Show resolved Hide resolved
/// {@macro resizablePane}
const ResizablePane({
ResizablePane({
super.key,
required this.builder,
required ScrollableWidgetBuilder this.builder,
this.decoration,
this.maxSize = 500.0,
required this.minSize,
Expand All @@ -48,14 +50,46 @@ class ResizablePane extends StatefulWidget {
assert(
(startSize >= minSize) && (startSize <= maxSize),
'startSize must not be less than minSize or more than maxWidth',
);
) {
useScrollBar = true;
}

/// Create a [ResizablePane] without an internal [MacosScrollbar]
/// {@macro resizablePane}
GroovinChip marked this conversation as resolved.
Show resolved Hide resolved
ResizablePane.noScrollBar({
super.key,
required Widget this.child,
this.decoration,
this.maxSize = 500.0,
required this.minSize,
this.isResizable = true,
required this.resizableSide,
this.windowBreakpoint,
required this.startSize,
}) : assert(
maxSize >= minSize,
'minSize should not be more than maxSize.',
),
assert(
(startSize >= minSize) && (startSize <= maxSize),
'startSize must not be less than minSize or more than maxWidth',
) {
useScrollBar = false;
}

/// The builder that creates a child to display in this widget, which will
/// use the provided [_scrollController] to enable the scrollbar to work.
///
/// Pass the [scrollController] obtained from this method, to a scrollable
/// widget used in this method to work with the internal [MacosScrollbar].
final ScrollableWidgetBuilder builder;
late final ScrollableWidgetBuilder? builder;

/// The child to display in this widget.
/// this is only used when the constructor used is [ResizablePane.noScrollbar]
stMerlHin marked this conversation as resolved.
Show resolved Hide resolved
late final Widget? child;
GroovinChip marked this conversation as resolved.
Show resolved Hide resolved

/// Specify if this [ResizablePane] should have an internal [MacosScrollbar]
stMerlHin marked this conversation as resolved.
Show resolved Hide resolved
late final bool useScrollBar;
stMerlHin marked this conversation as resolved.
Show resolved Hide resolved

/// The [BoxDecoration] to paint behind the child in the [builder].
final BoxDecoration? decoration;
Expand Down Expand Up @@ -277,10 +311,10 @@ class _ResizablePaneState extends State<ResizablePane> {
SafeArea(
left: false,
right: false,
child: MacosScrollbar(
child: widget.useScrollBar ? MacosScrollbar(
controller: _scrollController,
child: widget.builder(context, _scrollController),
),
child: widget.builder!(context, _scrollController),
) : widget.child!,
),
if (widget.isResizable && !_resizeOnRight && !_resizeOnTop)
Positioned(
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: macos_ui
description: Flutter widgets and themes implementing the current macOS design language.
version: 2.0.0-beta.1
version: 2.0.0-beta.2
homepage: "https://macosui.dev"
repository: "https://github.com/GroovinChip/macos_ui"

Expand Down
240 changes: 236 additions & 4 deletions test/layout/resizeable_pane_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ void main() {
const matrix = ResizableSide.values;

group('ResizablePane', () {
const double maxSize = 300;
const double minSize = 100;
const double startSize = 200;

for (var side in matrix) {
bool verticallyResizable = side == ResizableSide.top;

Expand All @@ -14,10 +18,6 @@ void main() {
? 'top'
: (side == ResizableSide.left ? 'left' : 'right'),
() {
const double maxSize = 300;
const double minSize = 100;
const double startSize = 200;

final resizablePane = ResizablePane(
builder: (context, scrollController) => const Text('Hello there'),
minSize: minSize,
Expand Down Expand Up @@ -79,6 +79,238 @@ void main() {
final double safeDelta = 50.0 * directionModifier;
final double overflowDelta = 500.0 * directionModifier;

testWidgets(
'Default ResizablePane Constructor comes with an internal MacosScrollBar',
(WidgetTester tester) async {
await tester.pumpWidget(view);
expect(find.byType(MacosScrollbar), findsOneWidget);
},
);

testWidgets('initial size equals startSize', (tester) async {
await tester.pumpWidget(view);

var resizablePaneRenderObject =
tester.renderObject<RenderBox>(resizablePaneFinder);
var initialSize = verticallyResizable
? resizablePaneRenderObject.size.height
: resizablePaneRenderObject.size.width;

expect(initialSize, startSize);
});

testWidgets('dragging wider works $side', (tester) async {
await tester.pumpWidget(view);

await tester.drag(
dragFinder,
verticallyResizable ? Offset(0, safeDelta) : Offset(safeDelta, 0),
);
await tester.pump();

var resizablePaneRenderObject =
tester.renderObject<RenderBox>(resizablePaneFinder);
expect(
verticallyResizable
? resizablePaneRenderObject.size.height
: resizablePaneRenderObject.size.width,
startSize + safeDelta * directionModifier,
);
});

testWidgets('dragging wider respects maxSize', (tester) async {
await tester.pumpWidget(view);

await tester.drag(
dragFinder,
verticallyResizable
? Offset(0, overflowDelta)
: Offset(overflowDelta, 0),
);
await tester.pump();

var resizablePaneRenderObject =
tester.renderObject<RenderBox>(resizablePaneFinder);
var currentSize = verticallyResizable
? resizablePaneRenderObject.size.height
: resizablePaneRenderObject.size.width;
expect(currentSize, maxSize);
});

testWidgets(
'drag events past maxSize have no effect $side',
(tester) async {
await tester.pumpWidget(view);

final dragStartLocation = tester.getCenter(dragFinder);
final drag = await tester.startGesture(dragStartLocation);
await drag.moveBy(
verticallyResizable
? Offset(0, overflowDelta)
: Offset(overflowDelta, 0),
);
await drag.moveBy(
verticallyResizable
? Offset(0, -10.0 * directionModifier)
: Offset(-10.0 * directionModifier, 0),
);
await drag.up();
await tester.pump();

var resizablePaneRenderObject =
tester.renderObject<RenderBox>(resizablePaneFinder);
var currentSize = verticallyResizable
? resizablePaneRenderObject.size.height
: resizablePaneRenderObject.size.width;
expect(currentSize, maxSize);
},
);

testWidgets('dragging narrower works', (tester) async {
await tester.pumpWidget(view);

await tester.drag(
dragFinder,
verticallyResizable
? Offset(0, -safeDelta)
: Offset(-safeDelta, 0),
);
await tester.pump();

var resizablePaneRenderObject =
tester.renderObject<RenderBox>(resizablePaneFinder);
var currentSize = verticallyResizable
? resizablePaneRenderObject.size.height
: resizablePaneRenderObject.size.width;
expect(
currentSize,
startSize - safeDelta * directionModifier,
);
});

testWidgets('dragging narrower respects minSize', (tester) async {
await tester.pumpWidget(view);

await tester.drag(
dragFinder,
verticallyResizable
? Offset(0, -overflowDelta)
: Offset(-overflowDelta, 0),
);
await tester.pump();

var resizablePaneRenderObject =
tester.renderObject<RenderBox>(resizablePaneFinder);
var currentSize = verticallyResizable
? resizablePaneRenderObject.size.height
: resizablePaneRenderObject.size.width;
expect(currentSize, minSize);
});

testWidgets(
'drag events past minSize have no effect',
(tester) async {
await tester.pumpWidget(view);

final dragStartLocation = tester.getCenter(dragFinder);
final drag = await tester.startGesture(dragStartLocation);
await drag.moveBy(
verticallyResizable
? Offset(0, -overflowDelta)
: Offset(-overflowDelta, 0),
);
await drag.moveBy(
verticallyResizable
? Offset(0, 10.0 * directionModifier)
: Offset(10.0 * directionModifier, 0),
);
await drag.up();
await tester.pump();

var resizablePaneRenderObject =
tester.renderObject<RenderBox>(resizablePaneFinder);
var currentSize = verticallyResizable
? resizablePaneRenderObject.size.height
: resizablePaneRenderObject.size.width;
expect(currentSize, minSize);
},
);
},
);
group(
side == ResizableSide.top
? 'top'
: (side == ResizableSide.left ? 'left' : 'right'),
() {
final resizablePane = ResizablePane.noScrollBar(
minSize: minSize,
startSize: startSize,
maxSize: maxSize,
resizableSide: side,
child: const Text('Hello there'),
);

final view = side == ResizableSide.top
? MacosApp(
home: MacosWindow(
disableWallpaperTinting: true,
child: MacosScaffold(
children: [
ContentArea(
builder: (context, scrollController) {
return Column(
children: [
const Flexible(
fit: FlexFit.loose,
child: Center(
child: Text('Hello there'),
),
),
resizablePane,
],
);
},
),
],
),
),
)
: MacosApp(
home: MacosWindow(
disableWallpaperTinting: true,
child: MacosScaffold(
children: [
resizablePane,
ContentArea(
builder: (context, scrollController) {
return const Text('Hello there');
},
),
],
),
),
);

final resizablePaneFinder = find.byWidget(resizablePane);
final dragFinder = find.descendant(
of: resizablePaneFinder,
matching: find.byType(GestureDetector),
);

// No need to check if the resizable side is top because directionModifier
// would take -1 if it is the case
final directionModifier = side == ResizableSide.right ? 1 : -1;
final double safeDelta = 50.0 * directionModifier;
final double overflowDelta = 500.0 * directionModifier;

testWidgets(
'ResizablePane.noScrollBar Constructor does not come with an internal MacosScrollBar',
(WidgetTester tester) async {
await tester.pumpWidget(view);
expect(find.byType(MacosScrollbar), findsNothing);
},
);

testWidgets('initial size equals startSize', (tester) async {
await tester.pumpWidget(view);

Expand Down