mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-06-22 10:02:20 +00:00
Feature: Make the address book & accessible devices side panels resizable to some degree
Replace the fixed-width left panels in the Address book and Accessible devices tabs with a draggable splitter. The chosen width is clamped to 120-300px and remembered as a local option so it survives restarts. The divider is invisible but still shows the resize cursor on hover. Signed-off-by: StealUrKill <35749471+StealUrKill@users.noreply.github.com>
This commit is contained in:
parent
3d478c4935
commit
aad1082595
3 changed files with 92 additions and 34 deletions
|
|
@ -9,6 +9,7 @@ import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
|||
import 'package:flutter_hbb/common/widgets/peer_card.dart';
|
||||
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/common/widgets/resizable_side_panel.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
||||
import 'package:flutter_hbb/models/ab_model.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
|
|
@ -36,6 +37,8 @@ class AddressBook extends StatefulWidget {
|
|||
|
||||
class _AddressBookState extends State<AddressBook> {
|
||||
var menuPos = RelativeRect.fill;
|
||||
final _tagsPanel = ResizablePanelController(
|
||||
optionKey: kOptionAbTagsPanelWidth, defaultWidth: 200, maxWidth: 300);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Obx(() {
|
||||
|
|
@ -77,33 +80,32 @@ class _AddressBookState extends State<AddressBook> {
|
|||
children: [
|
||||
Offstage(
|
||||
offstage: hideAbTagsPanel.value,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.background)),
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildAbDropdown(),
|
||||
_buildTagHeader().marginOnly(
|
||||
left: 8.0,
|
||||
right: gFFI.abModel.legacyMode.value ? 8.0 : 0,
|
||||
top: gFFI.abModel.legacyMode.value ? 8.0 : 0),
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: _buildTags(),
|
||||
child: Obx(() => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.background)),
|
||||
width: _tagsPanel.width.value,
|
||||
height: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildAbDropdown(),
|
||||
_buildTagHeader().marginOnly(
|
||||
left: 8.0,
|
||||
right: gFFI.abModel.legacyMode.value ? 8.0 : 0,
|
||||
top: gFFI.abModel.legacyMode.value ? 8.0 : 0),
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: _buildTags(),
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildAbPermission(),
|
||||
],
|
||||
),
|
||||
),
|
||||
).marginOnly(right: 12.0)),
|
||||
_buildAbPermission(),
|
||||
],
|
||||
),
|
||||
))),
|
||||
if (!hideAbTagsPanel.value) _tagsPanel.buildDivider(),
|
||||
_buildPeersViews()
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
|||
import 'package:flutter_hbb/common/widgets/login.dart';
|
||||
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:flutter_hbb/common/widgets/resizable_side_panel.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
|
|
@ -26,6 +27,10 @@ class _MyGroupState extends State<MyGroup> {
|
|||
RxString get searchAccessibleItemNameText =>
|
||||
gFFI.groupModel.searchAccessibleItemNameText;
|
||||
static TextEditingController searchUserController = TextEditingController();
|
||||
final _devicesPanel = ResizablePanelController(
|
||||
optionKey: kOptionAccessibleDevicesPanelWidth,
|
||||
defaultWidth: 150,
|
||||
maxWidth: 300);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -60,13 +65,13 @@ class _MyGroupState extends State<MyGroup> {
|
|||
Widget _buildLandscape() {
|
||||
return Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border:
|
||||
Border.all(color: Theme.of(context).colorScheme.background)),
|
||||
child: Container(
|
||||
width: 150,
|
||||
Obx(
|
||||
() => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.background)),
|
||||
width: _devicesPanel.width.value,
|
||||
height: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
|
|
@ -81,7 +86,8 @@ class _MyGroupState extends State<MyGroup> {
|
|||
],
|
||||
),
|
||||
),
|
||||
).marginOnly(right: 12.0),
|
||||
),
|
||||
_devicesPanel.buildDivider(),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
|
|
|
|||
50
flutter/lib/common/widgets/resizable_side_panel.dart
Normal file
50
flutter/lib/common/widgets/resizable_side_panel.dart
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/dragable_divider.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
// Persisted-width option keys for the resizable left panels.
|
||||
const String kOptionAbTagsPanelWidth = 'ab-tags-panel-width';
|
||||
const String kOptionAccessibleDevicesPanelWidth = 'accessible-devices-panel-width';
|
||||
|
||||
class ResizablePanelController {
|
||||
final String optionKey;
|
||||
final double defaultWidth;
|
||||
final double minWidth;
|
||||
final double maxWidth;
|
||||
late final RxDouble width;
|
||||
|
||||
ResizablePanelController({
|
||||
required this.optionKey,
|
||||
required this.defaultWidth,
|
||||
this.minWidth = 120,
|
||||
this.maxWidth = 300,
|
||||
}) {
|
||||
final saved = double.tryParse(bind.mainGetLocalOption(key: optionKey));
|
||||
width =
|
||||
RxDouble((saved ?? defaultWidth).clamp(minWidth, maxWidth).toDouble());
|
||||
}
|
||||
|
||||
void _onDrag(double dx) {
|
||||
width.value = (width.value + dx).clamp(minWidth, maxWidth).toDouble();
|
||||
}
|
||||
|
||||
void _persist() {
|
||||
bind.mainSetLocalOption(
|
||||
key: optionKey, value: width.value.toStringAsFixed(0));
|
||||
}
|
||||
|
||||
Widget buildDivider() {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onHorizontalDragUpdate: (details) => _onDrag(details.delta.dx),
|
||||
onHorizontalDragEnd: (_) => _persist(),
|
||||
onHorizontalDragCancel: _persist,
|
||||
child: DraggableDivider(
|
||||
axis: Axis.vertical,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
color: Colors.transparent,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue