Skip to content

Commit

Permalink
Drag and Drop (#62)
Browse files Browse the repository at this point in the history
* Flutter 3.0.3

* Container click without ink passes coordinates in event

* Drag and Drop implementation
  • Loading branch information
FeodorFitsner authored Jul 10, 2022
1 parent c7333dc commit e21c6bf
Show file tree
Hide file tree
Showing 10 changed files with 376 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ for:

install:
- brew install cocoapods
- curl https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.0.2-stable.zip -o flutter_macos_stable.zip
- curl https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.0.3-stable.zip -o flutter_macos_stable.zip
- unzip -qq flutter_macos_stable.zip
- export PATH="$PATH:`pwd`/flutter/bin"
- flutter --version
Expand Down Expand Up @@ -204,7 +204,7 @@ for:

install:
- export LANG=en_US.UTF-8
- curl https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.0.2-stable.zip -o flutter_macos_stable.zip
- curl https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.0.3-stable.zip -o flutter_macos_stable.zip
- unzip -qq flutter_macos_stable.zip
- export PATH="$PATH:`pwd`/flutter/bin"
- flutter --version
Expand Down
2 changes: 1 addition & 1 deletion ci/install_flutter.ps1
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
$distPath = "$env:TEMP\flutter_windows_stable.zip"

Write-Host "Downloading Flutter SDK..."
(New-Object Net.WebClient).DownloadFile("https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.0.2-stable.zip", $distPath)
(New-Object Net.WebClient).DownloadFile("https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.0.3-stable.zip", $distPath)

Write-Host "Unpacking Flutter SDK..."
7z x $distPath -o"$env:SystemDrive\" | Out-Null
5 changes: 3 additions & 2 deletions client/lib/controls/container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,13 @@ class ContainerControl extends StatelessWidget {
cursor: SystemMouseCursors.click,
child: GestureDetector(
child: container,
onTap: () {
onTapDown: (details) {
debugPrint("Container ${control.id} clicked!");
ws.pageEventFromWeb(
eventTarget: control.id,
eventName: "click",
eventData: control.attrs["data"] ?? "");
eventData: control.attrString("data", "")! +
"${details.localPosition.dx}:${details.localPosition.dy} ${details.globalPosition.dx}:${details.globalPosition.dy}");
},
),
);
Expand Down
14 changes: 14 additions & 0 deletions client/lib/controls/create_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import 'clipboard.dart';
import 'column.dart';
import 'container.dart';
import 'divider.dart';
import 'drag_target.dart';
import 'draggable.dart';
import 'dropdown.dart';
import 'elevated_button.dart';
import 'floating_action_button.dart';
Expand Down Expand Up @@ -145,6 +147,18 @@ Widget createControl(Control? parent, String id, bool parentDisabled) {
control: controlView.control,
children: controlView.children,
parentDisabled: parentDisabled);
case ControlType.draggable:
return DraggableControl(
parent: parent,
control: controlView.control,
children: controlView.children,
parentDisabled: parentDisabled);
case ControlType.dragTarget:
return DragTargetControl(
parent: parent,
control: controlView.control,
children: controlView.children,
parentDisabled: parentDisabled);
case ControlType.card:
return CardControl(
parent: parent,
Expand Down
93 changes: 93 additions & 0 deletions client/lib/controls/drag_target.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import 'dart:convert';

import 'package:flet_view/controls/error.dart';
import 'package:flutter/material.dart';

import '../models/control.dart';
import '../web_socket_client.dart';
import 'create_control.dart';

class DragTargetControl extends StatelessWidget {
final Control? parent;
final Control control;
final List<Control> children;
final bool parentDisabled;

const DragTargetControl(
{Key? key,
this.parent,
required this.control,
required this.children,
required this.parentDisabled})
: super(key: key);

@override
Widget build(BuildContext context) {
debugPrint("DragTarget build: ${control.id}");

var group = control.attrString("group", "");
var contentCtrls =
children.where((c) => c.name == "content" && c.isVisible);
bool disabled = control.isDisabled || parentDisabled;

Widget? child = contentCtrls.isNotEmpty
? createControl(control, contentCtrls.first.id, disabled)
: null;

if (child == null) {
return const ErrorControl("DragTarget should have content.");
}

return DragTarget<String>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
debugPrint(
"DragTarget.builder ${control.id}: accepted=${accepted.length}, rejected=${rejected.length}");
return child;
},
onWillAccept: (data) {
debugPrint("DragTarget.onAccept ${control.id}: $data");
String srcGroup = "";
if (data != null) {
var jd = json.decode(data);
srcGroup = jd["group"] as String;
}
var groupsEqual = srcGroup == group;
ws.pageEventFromWeb(
eventTarget: control.id,
eventName: "will_accept",
eventData:
control.attrString("data", "")! + groupsEqual.toString());
return groupsEqual;
},
onAccept: (data) {
debugPrint("DragTarget.onAccept ${control.id}: $data");
var jd = json.decode(data);
var srcId = jd["id"] as String;
ws.pageEventFromWeb(
eventTarget: control.id,
eventName: "accept",
eventData: control.attrString("data", "")! + srcId);
},
// onAcceptWithDetails: (details) {
// debugPrint(
// "onAcceptWithDetails: ${details.data} ${details.offset}");
// },
onLeave: (data) {
debugPrint("DragTarget.onLeave ${control.id}: $data");
String srcId = "";
if (data != null) {
var jd = json.decode(data);
srcId = jd["id"] as String;
}
ws.pageEventFromWeb(
eventTarget: control.id,
eventName: "leave",
eventData: control.attrString("data", "")! + srcId);
},
);
}
}
72 changes: 72 additions & 0 deletions client/lib/controls/draggable.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import 'dart:convert';

import 'package:flet_view/controls/error.dart';
import 'package:flutter/material.dart';

import '../models/control.dart';
import 'create_control.dart';

class DraggableControl extends StatelessWidget {
final Control? parent;
final Control control;
final List<Control> children;
final bool parentDisabled;

const DraggableControl(
{Key? key,
this.parent,
required this.control,
required this.children,
required this.parentDisabled})
: super(key: key);

@override
Widget build(BuildContext context) {
debugPrint("DragTarget build: ${control.id}");

var group = control.attrString("group", "");
var contentCtrls =
children.where((c) => c.name == "content" && c.isVisible);
var contentWhenDraggingCtrls =
children.where((c) => c.name == "content_when_dragging" && c.isVisible);
var contentFeedbackCtrls =
children.where((c) => c.name == "content_feedback" && c.isVisible);
bool disabled = control.isDisabled || parentDisabled;

Widget? child = contentCtrls.isNotEmpty
? createControl(control, contentCtrls.first.id, disabled)
: null;

Widget? childWhenDragging = contentWhenDraggingCtrls.isNotEmpty
? createControl(control, contentWhenDraggingCtrls.first.id, disabled)
: null;

Widget? childFeedback = contentFeedbackCtrls.isNotEmpty
? createControl(control, contentFeedbackCtrls.first.id, disabled)
: null;

if (child == null) {
return const ErrorControl("Draggable should have content.");
}

var data = json.encode({"id": control.id, "group": group});

return Draggable<String>(
data: data,
child: MouseRegion(
cursor: SystemMouseCursors.grab,
child: child,
),
childWhenDragging: childWhenDragging,
feedback: MouseRegion(
cursor: SystemMouseCursors.grabbing,
child: childFeedback ?? Opacity(opacity: 0.5, child: child),
),
// dragAnchorStrategy: (d, context, offset) {
// debugPrint("dragAnchorStrategy: ${offset.dx}, ${offset.dy}");
// return offset;
// }
//feedbackOffset: const Offset(-30, -30),
);
}
}
2 changes: 2 additions & 0 deletions client/lib/models/control_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ enum ControlType {
column,
container,
divider,
draggable,
dragTarget,
dropdown,
dropdownOption,
elevatedButton,
Expand Down
2 changes: 2 additions & 0 deletions sdk/python/flet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from flet.container import Container
from flet.control import Control
from flet.divider import Divider
from flet.drag_target import DragTarget
from flet.draggable import Draggable
from flet.dropdown import Dropdown
from flet.elevated_button import ElevatedButton
from flet.filled_button import FilledButton
Expand Down
94 changes: 94 additions & 0 deletions sdk/python/flet/drag_target.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from beartype import beartype

from flet.control import Control
from flet.ref import Ref


class DragTarget(Control):
def __init__(
self,
ref: Ref = None,
disabled: bool = None,
visible: bool = None,
data: any = None,
#
# Specific
#
group: str = None,
content: Control = None,
on_will_accept=None,
on_accept=None,
on_leave=None,
):

Control.__init__(
self,
ref=ref,
disabled=disabled,
visible=visible,
data=data,
)

self.__content: Control = None

self.group = group
self.content = content
self.on_will_accept = on_will_accept
self.on_accept = on_accept
self.on_leave = on_leave

def _get_control_name(self):
return "dragtarget"

def _get_children(self):
children = []
if self.__content:
self.__content._set_attr_internal("n", "content")
children.append(self.__content)
return children

# group
@property
def group(self):
return self._get_attr("group")

@group.setter
@beartype
def group(self, value):
self._set_attr("group", value)

# content
@property
def content(self):
return self.__content

@content.setter
def content(self, value):
self.__content = value

# on_will_accept
@property
def on_will_accept(self):
return self._get_event_handler("will_accept")

@on_will_accept.setter
def on_will_accept(self, handler):
self._add_event_handler("will_accept", handler)

# on_accept
@property
def on_accept(self):
return self._get_event_handler("accept")

@on_accept.setter
def on_accept(self, handler):
self._add_event_handler("accept", handler)

# on_leave
@property
def on_leave(self):
return self._get_event_handler("leave")

@on_leave.setter
def on_leave(self, handler):
self._add_event_handler("leave", handler)
Loading

0 comments on commit e21c6bf

Please sign in to comment.