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

Markdown and ShaderMask controls #160

Merged
merged 14 commits into from
Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ skip_commits:
- "*.md"

environment:
GO_VERSION: 1.18.3
GO_VERSION: 1.19
GO_TAGS: --tags release
python_version: 3.10
GITHUB_TOKEN:
Expand Down
Binary file modified client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/assets/icon/flet-android-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/assets/icon/flet-ios-1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion client/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -539,4 +539,4 @@
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 15 additions & 5 deletions client/lib/controls/clipboard.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,36 @@ import 'package:flutter/widgets.dart';

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

class ClipboardControl extends StatelessWidget {
class ClipboardControl extends StatefulWidget {
final Control? parent;
final Control control;

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

@override
State<ClipboardControl> createState() => _ClipboardControlState();
}

class _ClipboardControlState extends State<ClipboardControl> {
String _ts = "";

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

var value = control.attrString("value");
var value = widget.control.attrString("value");

if (value != null) {
debugPrint("Clipboard JSON value: $value");

var jv = json.decode(value);
var ts = jv["ts"] as String;
var text = jv["d"] as String?;
if (text != null) {
if (text != null && ts != _ts) {
Clipboard.setData(ClipboardData(text: text));
_ts = ts;
}
}

Expand Down
36 changes: 32 additions & 4 deletions client/lib/controls/container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ContainerControl extends StatelessWidget {
bool ink = control.attrBool("ink", false)!;
bool onClick = control.attrBool("onclick", false)!;
bool onLongPress = control.attrBool("onLongPress", false)!;
bool onHover = control.attrBool("onHover", false)!;
bool disabled = control.isDisabled || parentDisabled;

var boxDecor = BoxDecoration(
Expand All @@ -46,13 +47,13 @@ class ContainerControl extends StatelessWidget {
? createControl(control, contentCtrls.first.id, disabled)
: null;

if ((onClick || onLongPress) && ink) {
if ((onClick || onLongPress || onHover) && ink) {
return constrainedControl(
Container(
margin: parseEdgeInsets(control, "margin"),
child: Ink(
child: InkWell(
onTap: onClick
onTap: onClick || onHover
? () {
debugPrint("Container ${control.id} clicked!");
ws.pageEventFromWeb(
Expand All @@ -61,7 +62,7 @@ class ContainerControl extends StatelessWidget {
eventData: control.attrs["data"] ?? "");
}
: null,
onLongPress: onLongPress
onLongPress: onLongPress || onHover
? () {
debugPrint("Container ${control.id} long pressed!");
ws.pageEventFromWeb(
Expand All @@ -70,6 +71,15 @@ class ContainerControl extends StatelessWidget {
eventData: control.attrs["data"] ?? "");
}
: null,
onHover: onHover
? (value) {
debugPrint("Container ${control.id} hovered!");
ws.pageEventFromWeb(
eventTarget: control.id,
eventName: "hover",
eventData: value.toString());
}
: null,
child: Container(
child: child,
padding: parseEdgeInsets(control, "padding"),
Expand All @@ -88,9 +98,27 @@ class ContainerControl extends StatelessWidget {
decoration: boxDecor,
child: child);

if (onClick || onLongPress) {
if (onClick || onLongPress || onHover) {
container = MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: onHover
? (value) {
debugPrint("Container's mouse region ${control.id} entered!");
ws.pageEventFromWeb(
eventTarget: control.id,
eventName: "hover",
eventData: "true");
}
: null,
onExit: onHover
? (value) {
debugPrint("Container's mouse region ${control.id} exited!");
ws.pageEventFromWeb(
eventTarget: control.id,
eventName: "hover",
eventData: "false");
}
: null,
child: GestureDetector(
child: container,
onTapDown: onClick
Expand Down
30 changes: 23 additions & 7 deletions client/lib/controls/create_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import 'grid_view.dart';
import 'icon.dart';
import 'icon_button.dart';
import 'image.dart';
import 'launch_url.dart';
import 'list_tile.dart';
import 'list_view.dart';
import 'markdown.dart';
import 'navigation_rail.dart';
import 'outlined_button.dart';
import 'page.dart';
Expand All @@ -34,6 +36,7 @@ import 'progress_ring.dart';
import 'radio.dart';
import 'radio_group.dart';
import 'row.dart';
import 'shader_mask.dart';
import 'slider.dart';
import 'snack_bar.dart';
import 'stack.dart';
Expand Down Expand Up @@ -68,27 +71,34 @@ Widget createControl(Control? parent, String id, bool parentDisabled) {
children: controlView.children,
dispatch: controlView.dispatch);
case ControlType.text:
return TextControl(control: controlView.control);
return TextControl(parent: parent, control: controlView.control);
case ControlType.icon:
return IconControl(control: controlView.control);
return IconControl(parent: parent, control: controlView.control);
case ControlType.markdown:
return MarkdownControl(parent: parent, control: controlView.control);
case ControlType.clipboard:
return ClipboardControl(control: controlView.control);
return ClipboardControl(parent: parent, control: controlView.control);
case ControlType.launchUrl:
return LaunchUrlControl(parent: parent, control: controlView.control);
case ControlType.image:
return ImageControl(parent: parent, control: controlView.control);
case ControlType.divider:
return DividerControl(control: controlView.control);
return DividerControl(parent: parent, control: controlView.control);
case ControlType.verticalDivider:
return VerticalDividerControl(control: controlView.control);
return VerticalDividerControl(
parent: parent, control: controlView.control);
case ControlType.circleAvatar:
return CircleAvatarControl(
parent: parent,
control: controlView.control,
children: controlView.children,
parentDisabled: parentDisabled);
case ControlType.progressRing:
return ProgressRingControl(control: controlView.control);
return ProgressRingControl(
parent: parent, control: controlView.control);
case ControlType.progressBar:
return ProgressBarControl(control: controlView.control);
return ProgressBarControl(
parent: parent, control: controlView.control);
case ControlType.elevatedButton:
return ElevatedButtonControl(
parent: parent,
Expand Down Expand Up @@ -167,6 +177,12 @@ Widget createControl(Control? parent, String id, bool parentDisabled) {
control: controlView.control,
children: controlView.children,
parentDisabled: parentDisabled);
case ControlType.shaderMask:
return ShaderMaskControl(
parent: parent,
control: controlView.control,
children: controlView.children,
parentDisabled: parentDisabled);
case ControlType.listTile:
return ListTileControl(
parent: parent,
Expand Down
2 changes: 1 addition & 1 deletion client/lib/controls/divider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class DividerControl extends StatelessWidget {
final Control? parent;
final Control control;

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

@override
Expand Down
5 changes: 0 additions & 5 deletions client/lib/controls/elevated_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ class ElevatedButtonControl extends StatelessWidget {
IconData? icon = getMaterialIcon(control.attrString("icon", "")!);
Color? iconColor = HexColor.fromString(
Theme.of(context), control.attrString("iconColor", "")!);
Color? color = HexColor.fromString(
Theme.of(context), control.attrString("color", "")!);
Color? bgcolor = HexColor.fromString(
Theme.of(context), control.attrString("bgcolor", "")!);
var elevation = control.attrDouble("elevation");
var contentCtrls = children.where((c) => c.name == "content");
bool autofocus = control.attrBool("autofocus", false)!;
bool disabled = control.isDisabled || parentDisabled;
Expand Down
2 changes: 1 addition & 1 deletion client/lib/controls/icon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class IconControl extends StatelessWidget {
final Control? parent;
final Control control;

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

@override
Expand Down
59 changes: 33 additions & 26 deletions client/lib/controls/image.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_redux/flutter_redux.dart';

Expand All @@ -12,16 +15,18 @@ class ImageControl extends StatelessWidget {
final Control? parent;
final Control control;

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

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

var src = control.attrString("src", "")!;
if (src == "") {
return const ErrorControl("Image must have 'src' specified.");
var srcBase64 = control.attrString("srcBase64", "")!;
if (src == "" && srcBase64 == "") {
return const ErrorControl(
"Image must have 'src' or 'src_base64' specified.");
}

double? width = control.attrDouble("width", null);
Expand All @@ -38,29 +43,31 @@ class ImageControl extends StatelessWidget {
orElse: () => BoxFit.none);

var uri = Uri.parse(src);
if (!uri.hasAuthority) {
// wrap into StoreConnector
return StoreConnector<AppState, Uri?>(
distinct: true,
converter: (store) => store.state.pageUri,
builder: (context, pageUri) {
return baseControl(
_clipCorners(
Image.network(getAssetUri(pageUri!, src).toString(),
width: width, height: height, repeat: repeat, fit: fit),
control),
parent,
control);
});
} else {
return baseControl(
_clipCorners(
Image.network(src,
width: width, height: height, repeat: repeat, fit: fit),
control),
parent,
control);
}
return StoreConnector<AppState, Uri?>(
distinct: true,
converter: (store) => store.state.pageUri,
builder: (context, pageUri) {
Image? image;

if (srcBase64 != "") {
try {
Uint8List bytes = base64Decode(srcBase64);
image = Image.memory(bytes,
width: width, height: height, repeat: repeat, fit: fit);
} catch (ex) {
return ErrorControl("Error decoding base64: ${ex.toString()}");
}
} else {
image = Image.network(
uri.hasAuthority ? src : getAssetUri(pageUri!, src).toString(),
width: width,
height: height,
repeat: repeat,
fit: fit);
}

return baseControl(_clipCorners(image!, control), parent, control);
});
}

Widget _clipCorners(Widget image, Control control) {
Expand Down
43 changes: 43 additions & 0 deletions client/lib/controls/launch_url.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'dart:convert';

import 'package:flutter/widgets.dart';
import 'package:url_launcher/url_launcher.dart';

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

class LaunchUrlControl extends StatefulWidget {
final Control? parent;
final Control control;

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

@override
State<LaunchUrlControl> createState() => _LaunchUrlControlState();
}

class _LaunchUrlControlState extends State<LaunchUrlControl> {
String _ts = "";

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

var value = widget.control.attrString("value");

if (value != null) {
debugPrint("Launch URL JSON value: $value");

var jv = json.decode(value);
var ts = jv["ts"] as String;
var url = jv["url"] as String?;
if (url != null && ts != _ts) {
launchUrl(Uri.parse(url));
_ts = ts;
}
}

return const SizedBox.shrink();
}
}
Loading