Merge pull request 'feat: add CopyFunc to provider generator; add local solution provider; update dde data table style.' (#2) from dev into master

Reviewed-on: #2
master
DebuggerX 4 years ago
commit dd8e6fced1

@ -0,0 +1,4 @@
root = true
[*.dart]
max_line_length = 120

@ -1,5 +0,0 @@
.dart_tool
.packages
.flutter-plugins
.flutter-plugins-dependencies
pubspec.lock

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -1,40 +0,0 @@
# window_size
This plugin allows resizing and repositioning the window containing the Flutter
content, as well as querying screen information.
This is a prototype, and in the long term is expected to be replaced by
[functionality within the Flutter
framework](https://flutter.dev/go/desktop-multi-window-support).
## Scope
There are currently no plans to add new functionality, such as window
minimization and maximization, to this plugin. The goals of this plugin were to:
- unblock certain core use cases among very early adopters, and
- validate plugin APIs in Flutter itself during early development of the desktop
plugin APIs.
Now that those goals have been met, and the plugin APIs have been stabilized
such that anyone can create and publish desktop Flutter plugins, new functionality
will likely only be added here if unblocks a [Flutter top-tier
customer](https://github.com/flutter/flutter/wiki/Issue-hygiene#customers).
The community is encouraged to create their own plugins for other window
manipulation features.
## Supported Platforms
- [x] macOS
- [x] Windows
- [x] Linux
Not all operations have been implemented on all platforms, but the core functionality
of resizing and repositioning is available for all three.
## Use
See [the plugin README](../README.md) for general instructions on using FDE plugins.
### Linux
Requires GTK 3.22 or later.

@ -1,126 +0,0 @@
# Copyright 2017 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Root analysis options shared among all Dart code in the respository. Based
# on the Fuchsia standard analysis options, with some changes.
linter:
# Full list available at http://dart-lang.github.io/linter/lints/options/options.html.
rules:
- always_declare_return_types
- always_put_control_body_on_new_line
- always_put_required_named_parameters_first
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_catches_without_on_clauses
- avoid_catching_errors
- avoid_classes_with_only_static_members
- avoid_empty_else
# Not compatible with VS Code yet.
# - avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_positional_boolean_parameters
- avoid_private_typedef_functions
# TODO: Change relative imports for package imports
# - avoid_relative_lib_imports
# This puts an unnecessary burden on API clients.
# - avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_returning_null
- avoid_returning_this
- avoid_single_cascade_in_expression_statements
- avoid_setters_without_getters
- avoid_slow_async_io
- avoid_types_as_parameter_names
- avoid_types_on_closure_parameters
- avoid_unused_constructor_parameters
- await_only_futures
- camel_case_types
- cancel_subscriptions
- cascade_invocations
- close_sinks
- comment_references
- constant_identifier_names
- control_flow_in_finally
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
- hash_and_equals
- implementation_imports
- invariant_booleans
- iterable_contains_unrelated_type
- join_return_with_assignment
- library_names
- library_prefixes
- list_remove_unrelated_type
- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_duplicate_case_values
- non_constant_identifier_names
- omit_local_variable_types
- one_member_abstracts
- only_throw_errors
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
- parameter_assignments
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_collection_literals
- prefer_conditional_assignment
# Disabled until bug is fixed
# https://github.com/dart-lang/linter/issues/995
# - prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_constructors_over_static_methods
- prefer_contains
- prefer_equal_for_default_values
# Add this when 'short' is better defined.
# - prefer_expression_function_bodies
- prefer_final_fields
- prefer_final_locals
# Seems to have false positive with await for.
# - prefer_foreach
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_initializing_formals
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_iterable_whereType
- prefer_single_quotes
- prefer_typing_uninitialized_variables
- public_member_api_docs
- recursive_getters
- slash_for_doc_comments
- sort_constructors_first
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- type_annotate_public_apis
- type_init_formals
- unawaited_futures
- unnecessary_brace_in_string_interps
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_statements
- unnecessary_this
- unrelated_type_equality_checks
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_string_buffers
- use_to_and_as_if_applicable
- valid_regexps
# Not compatible with VS Code yet.
# - void_checks

@ -1,32 +0,0 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:ui';
import 'screen.dart';
/// Represents a window, containing information about its size, position, and
/// properties.
class PlatformWindow {
/// Create a new window.
PlatformWindow(this.frame, this.scaleFactor, this.screen);
/// The frame of the screen, in screen coordinates.
final Rect frame;
/// The number of pixels per screen coordinate for this screen.
final double scaleFactor;
/// The (or a) screen containing this window, if any.
final Screen? screen;
}

@ -1,31 +0,0 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:ui';
/// Represents a screen, containing information about its size, position, and
/// properties.
class Screen {
/// Create a new screen.
Screen(this.frame, this.visibleFrame, this.scaleFactor);
/// The frame of the screen, in screen coordinates.
final Rect frame;
/// The portion of the screen's frame that is available for use by application
/// windows. E.g., on macOS, this excludes the menu bar.
final Rect visibleFrame;
/// The number of pixels per screen coordinate for this screen.
final double scaleFactor;
}

@ -1,249 +0,0 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:async';
import 'dart:ui';
import 'package:flutter/services.dart';
import 'platform_window.dart';
import 'screen.dart';
/// The name of the plugin's platform channel.
const String _windowSizeChannelName = 'flutter/windowsize';
/// The method name to request information about the available screens.
///
/// Returns a list of screen info maps; see keys below.
const String _getScreenListMethod = 'getScreenList';
/// The method name to request information about the window containing the
/// Flutter instance.
///
/// Returns a list of window info maps; see keys below.
const String _getWindowInfoMethod = 'getWindowInfo';
/// The method name to set the frame of a window.
///
/// Takes a frame array, as documented for the value of _frameKey.
const String _setWindowFrameMethod = 'setWindowFrame';
/// The method name to set the minimum size of a window.
///
/// Takes a window size array, with the value is a list of two doubles:
/// [width, height].
///
/// A value of zero for width or height is to be interpreted as
/// unconstrained in that dimension.
const String _setWindowMinimumSizeMethod = 'setWindowMinimumSize';
/// The method name to set the maximum size of a window.
///
/// Takes a window size array, with the value is a list of two doubles:
/// [width, height].
///
/// A value of `-1` for width or height is to be interpreted as
/// unconstrained in that dimension.
const String _setWindowMaximumSizeMethod = 'setWindowMaximumSize';
/// The method name to set the window title of a window.
const String _setWindowTitleMethod = 'setWindowTitle';
/// The method name to set the window title's represented URL.
///
/// Only implemented for macOS. If the URL is a file URL, the
/// window shows an icon in its title bar.
const String _setWindowTitleRepresentedUrlMethod =
'setWindowTitleRepresentedUrl';
/// The method name to get the minimum size of a window.
///
/// Returns a window size array, with the value is a list of two doubles:
/// [width, height].
///
/// A value of zero for width or height is to be interpreted as
/// unconstrained in that dimension.
const String _getWindowMinimumSizeMethod = 'getWindowMinimumSize';
/// The method name to get the maximum size of a window.
///
/// Returns a window size array, with the value is a list of two doubles:
/// [width, height].
///
/// A value of `-1` for width or height is to be interpreted as
/// unconstrained in that dimension.
const String _getWindowMaximumSizeMethod = 'getWindowMaximumSize';
/// The method name to set the window visibility.
///
/// The argument will be a boolean controlling whether or not the window should
/// be visible.
const String _setWindowVisibilityMethod = 'setWindowVisibility';
// Keys for screen and window maps returned by _getScreenListMethod.
/// The frame of a screen or window. The value is a list of four doubles:
/// [left, top, width, height]
const String _frameKey = 'frame';
/// The frame of a screen available for use by applications. The value format
/// is the same as _frameKey's.
///
/// Only used for screens.
const String _visibleFrameKey = 'visibleFrame';
/// The scale factor for a screen or window, as a double.
///
/// This is the number of pixels per screen coordinate, and thus the ratio
/// between sizes as seen by Flutter and sizes in native screen coordinates.
const String _scaleFactorKey = 'scaleFactor';
/// The screen containing this window, if any. The value is a screen map, or
/// null if the window is not visible on a screen.
///
/// Only used for windows.
///
/// If a window is on multiple screens, it is up to the platform to decide which
/// screen to report.
const String _screenKey = 'screen';
/// A singleton object that handles the interaction with the platform channel.
class WindowSizeChannel {
/// Private constructor.
WindowSizeChannel._();
final MethodChannel _platformChannel =
const MethodChannel(_windowSizeChannelName);
/// The static instance of the menu channel.
static final WindowSizeChannel instance = new WindowSizeChannel._();
/// Returns a list of screens.
Future<List<Screen>> getScreenList() async {
final screenList = <Screen>[];
final response = await _platformChannel.invokeMethod(_getScreenListMethod);
for (final screenInfo in response) {
screenList.add(_screenFromInfoMap(screenInfo));
}
return screenList;
}
/// Returns information about the window containing this Flutter instance.
Future<PlatformWindow> getWindowInfo() async {
final response = await _platformChannel.invokeMethod(_getWindowInfoMethod);
final screenInfo = response[_screenKey];
final screen = screenInfo == null ? null : _screenFromInfoMap(screenInfo);
return PlatformWindow(_rectFromLTWHList(response[_frameKey].cast<double>()),
response[_scaleFactorKey], screen);
}
/// Sets the frame of the window containing this Flutter instance, in
/// screen coordinates.
///
/// The platform may adjust the frame as necessary if the provided frame would
/// cause significant usability issues (e.g., a window with no visible portion
/// that can be used to move the window).
void setWindowFrame(Rect frame) async {
assert(!frame.isEmpty, 'Cannot set window frame to an empty rect.');
assert(frame.isFinite, 'Cannot set window frame to a non-finite rect.');
await _platformChannel.invokeMethod(_setWindowFrameMethod,
[frame.left, frame.top, frame.width, frame.height]);
}
/// Sets the minimum size of the window containing this Flutter instance.
void setWindowMinSize(Size size) async {
await _platformChannel
.invokeMethod(_setWindowMinimumSizeMethod, [size.width, size.height]);
}
/// Sets the visibility of the window.
void setWindowVisibility({required bool visible}) async {
await _platformChannel.invokeMethod(_setWindowVisibilityMethod, visible);
}
// Window maximum size unconstrained is passed over the channel as -1.
double _channelRepresentationForMaxDimension(double size) {
return size == double.infinity ? -1 : size;
}
/// Sets the maximum size of the window containing this Flutter instance.
void setWindowMaxSize(Size size) async {
await _platformChannel.invokeMethod(_setWindowMaximumSizeMethod, [
_channelRepresentationForMaxDimension(size.width),
_channelRepresentationForMaxDimension(size.height),
]);
}
/// Sets the title of the window containing this Flutter instance.
void setWindowTitle(String title) async {
await _platformChannel.invokeMapMethod(_setWindowTitleMethod, title);
}
/// Sets the title's represented URL of the window containing this Flutter instance.
void setWindowTitleRepresentedUrl(Uri file) async {
await _platformChannel.invokeMapMethod(
_setWindowTitleRepresentedUrlMethod, file.toString());
}
/// Gets the minimum size of the window containing this Flutter instance.
Future<Size> getWindowMinSize() async {
final response =
await _platformChannel.invokeMethod(_getWindowMinimumSizeMethod);
return _sizeFromWHList(List<double>.from(response.cast<double>()));
}
// Window maximum size unconstrained is passed over the channel as -1.
double _maxDimensionFromChannelRepresentation(double size) {
return size == -1 ? double.infinity : size;
}
/// Gets the maximum size of the window containing this Flutter instance.
Future<Size> getWindowMaxSize() async {
final response =
await _platformChannel.invokeMethod(_getWindowMaximumSizeMethod);
return _sizeFromWHList(
List<double>.from(
response.cast<double>().map(_maxDimensionFromChannelRepresentation),
),
);
}
/// Given an array of the form [left, top, width, height], return the
/// corresponding [Rect].
///
/// Used for frame deserialization in the platform channel.
Rect _rectFromLTWHList(List<double> ltwh) {
return Rect.fromLTWH(ltwh[0], ltwh[1], ltwh[2], ltwh[3]);
}
/// Given an array of the form [width, height], return the corresponding
/// [Size].
///
/// Used for window size deserialization in the platform channel.
Size _sizeFromWHList(List<double> wh) {
return Size(wh[0], wh[1]);
}
/// Given a map of information about a screen, return the corresponding
/// [Screen] object.
///
/// Used for screen deserialization in the platform channel.
Screen _screenFromInfoMap(Map<dynamic, dynamic> map) {
return Screen(
_rectFromLTWHList(map[_frameKey].cast<double>()),
_rectFromLTWHList(map[_visibleFrameKey].cast<double>()),
map[_scaleFactorKey]);
}
}

@ -1,89 +0,0 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:async';
import 'dart:ui';
import 'platform_window.dart';
import 'screen.dart';
import 'window_size_channel.dart';
/// Returns a list of [Screen]s for the current screen configuration.
///
/// It is possible for this list to be empty, if the machine is running in
/// a headless mode.
Future<List<Screen>> getScreenList() async {
return await WindowSizeChannel.instance.getScreenList();
}
/// Returns the [Screen] showing the window that contains this Flutter instance.
///
/// If the window is not being displayed, returns null. If the window is being
/// displayed on multiple screens, the platform can return any of those screens.
Future<Screen?> getCurrentScreen() async {
final windowInfo = await WindowSizeChannel.instance.getWindowInfo();
return windowInfo.screen;
}
/// Returns information about the window containing this Flutter instance.
Future<PlatformWindow> getWindowInfo() async {
return await WindowSizeChannel.instance.getWindowInfo();
}
/// Sets the frame of the window containing this Flutter instance, in
/// screen coordinates.
///
/// The platform may adjust the frame as necessary if the provided frame would
/// cause significant usability issues (e.g., a window with no visible portion
/// that can be used to move the window).
void setWindowFrame(Rect frame) async {
WindowSizeChannel.instance.setWindowFrame(frame);
}
/// Sets the minimum [Size] of the window containing this Flutter instance.
void setWindowMinSize(Size size) async {
WindowSizeChannel.instance.setWindowMinSize(size);
}
/// Sets the maximum [Size] of the window containing this Flutter instance.
void setWindowMaxSize(Size size) async {
WindowSizeChannel.instance.setWindowMaxSize(size);
}
/// Sets the window title, as a [String], of the window containing this Flutter instance.
void setWindowTitle(String title) async {
WindowSizeChannel.instance.setWindowTitle(title);
}
/// Shows or hides the window.
void setWindowVisibility({required bool visible}) async {
WindowSizeChannel.instance.setWindowVisibility(visible: visible);
}
/// Sets the window title's represented [Uri], of the window containing this Flutter instance.
///
/// Only implemented for macOS. If the URL is a file URL, the
/// window shows an icon in its title bar.
void setWindowTitleRepresentedUrl(Uri url) async {
WindowSizeChannel.instance.setWindowTitleRepresentedUrl(url);
}
/// Gets the minimum [Size] of the window containing this Flutter instance.
Future<Size> getWindowMinSize() async {
return WindowSizeChannel.instance.getWindowMinSize();
}
/// Gets the maximum [Size] of the window containing this Flutter instance.
Future<Size> getWindowMaxSize() async {
return WindowSizeChannel.instance.getWindowMaxSize();
}

@ -1,16 +0,0 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
export 'src/platform_window.dart';
export 'src/screen.dart';
export 'src/window_size_utils.dart';

@ -1,17 +0,0 @@
cmake_minimum_required(VERSION 3.10)
set(PROJECT_NAME "window_size")
project(${PROJECT_NAME} LANGUAGES CXX)
set(PLUGIN_NAME "${PROJECT_NAME}_plugin")
add_library(${PLUGIN_NAME} SHARED
"${PLUGIN_NAME}.cc"
)
apply_standard_settings(${PLUGIN_NAME})
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter)
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)

@ -1,40 +0,0 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLUGINS_WINDOW_SIZE_LINUX_WINDOW_SIZE_PLUGIN_H_
#define PLUGINS_WINDOW_SIZE_LINUX_WINDOW_SIZE_PLUGIN_H_
// A plugin to allow resizing the window.
#include <flutter_linux/flutter_linux.h>
G_BEGIN_DECLS
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
#else
#define FLUTTER_PLUGIN_EXPORT
#endif
G_DECLARE_FINAL_TYPE(FlWindowSizePlugin, fl_window_size_plugin, FL,
WINDOW_SIZE_PLUGIN, GObject)
FLUTTER_PLUGIN_EXPORT FlWindowSizePlugin* fl_window_size_plugin_new(
FlPluginRegistrar* registrar);
FLUTTER_PLUGIN_EXPORT void window_size_plugin_register_with_registrar(
FlPluginRegistrar* registrar);
G_END_DECLS
#endif // PLUGINS_WINDOW_SIZE_LINUX_WINDOW_SIZE_PLUGIN_H_

@ -1,411 +0,0 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "include/window_size/window_size_plugin.h"
#include <flutter_linux/flutter_linux.h>
#include <gtk/gtk.h>
// See window_size_channel.dart for documentation.
const char kChannelName[] = "flutter/windowsize";
const char kBadArgumentsError[] = "Bad Arguments";
const char kNoScreenError[] = "No Screen";
const char kGetScreenListMethod[] = "getScreenList";
const char kGetWindowInfoMethod[] = "getWindowInfo";
const char kSetWindowFrameMethod[] = "setWindowFrame";
const char kSetWindowMinimumSizeMethod[] = "setWindowMinimumSize";
const char kSetWindowMaximumSizeMethod[] = "setWindowMaximumSize";
const char kSetWindowTitleMethod[] = "setWindowTitle";
const char ksetWindowVisibilityMethod[] = "setWindowVisibility";
const char kGetWindowMinimumSizeMethod[] = "getWindowMinimumSize";
const char kGetWindowMaximumSizeMethod[] = "getWindowMaximumSize";
const char kFrameKey[] = "frame";
const char kVisibleFrameKey[] = "visibleFrame";
const char kScaleFactorKey[] = "scaleFactor";
const char kScreenKey[] = "screen";
struct _FlWindowSizePlugin {
GObject parent_instance;
FlPluginRegistrar* registrar;
// Connection to Flutter engine.
FlMethodChannel* channel;
// Requested window geometry.
GdkGeometry window_geometry;
};
G_DEFINE_TYPE(FlWindowSizePlugin, fl_window_size_plugin, g_object_get_type())
// Gets the window being controlled.
GtkWindow* get_window(FlWindowSizePlugin* self) {
FlView* view = fl_plugin_registrar_get_view(self->registrar);
if (view == nullptr) return nullptr;
return GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
}
// Gets the display connection.
GdkDisplay* get_display(FlWindowSizePlugin* self) {
FlView* view = fl_plugin_registrar_get_view(self->registrar);
if (view == nullptr) return nullptr;
return gtk_widget_get_display(GTK_WIDGET(view));
}
// Converts frame dimensions into the Flutter representation.
FlValue* make_frame_value(gint x, gint y, gint width, gint height) {
g_autoptr(FlValue) value = fl_value_new_list();
fl_value_append_take(value, fl_value_new_float(x));
fl_value_append_take(value, fl_value_new_float(y));
fl_value_append_take(value, fl_value_new_float(width));
fl_value_append_take(value, fl_value_new_float(height));
return fl_value_ref(value);
}
// Converts monitor information into the Flutter representation.
FlValue* make_monitor_value(GdkMonitor* monitor) {
g_autoptr(FlValue) value = fl_value_new_map();
GdkRectangle frame;
gdk_monitor_get_geometry(monitor, &frame);
fl_value_set_string_take(
value, kFrameKey,
make_frame_value(frame.x, frame.y, frame.width, frame.height));
gdk_monitor_get_workarea(monitor, &frame);
fl_value_set_string_take(
value, kVisibleFrameKey,
make_frame_value(frame.x, frame.y, frame.width, frame.height));
gint scale_factor = gdk_monitor_get_scale_factor(monitor);
fl_value_set_string_take(value, kScaleFactorKey,
fl_value_new_float(scale_factor));
return fl_value_ref(value);
}
// Gets the list of current screens.
static FlMethodResponse* get_screen_list(FlWindowSizePlugin* self) {
g_autoptr(FlValue) screens = fl_value_new_list();
GdkDisplay* display = get_display(self);
if (display == nullptr) {
return FL_METHOD_RESPONSE(
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
}
gint n_monitors = gdk_display_get_n_monitors(display);
for (gint i = 0; i < n_monitors; i++) {
GdkMonitor* monitor = gdk_display_get_monitor(display, i);
fl_value_append_take(screens, make_monitor_value(monitor));
}
return FL_METHOD_RESPONSE(fl_method_success_response_new(screens));
}
// Gets information about the Flutter window.
static FlMethodResponse* get_window_info(FlWindowSizePlugin* self) {
GtkWindow* window = get_window(self);
if (window == nullptr) {
return FL_METHOD_RESPONSE(
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
}
g_autoptr(FlValue) window_info = fl_value_new_map();
gint x, y, width, height;
gtk_window_get_position(window, &x, &y);
gtk_window_get_size(window, &width, &height);
fl_value_set_string_take(window_info, kFrameKey,
make_frame_value(x, y, width, height));
// Get the monitor this window is inside, or the primary monitor if doesn't
// appear to be in any.
GdkDisplay* display = get_display(self);
GdkMonitor* monitor_with_window = gdk_display_get_primary_monitor(display);
int n_monitors = gdk_display_get_n_monitors(display);
for (int i = 0; i < n_monitors; i++) {
GdkMonitor* monitor = gdk_display_get_monitor(display, i);
GdkRectangle frame;
gdk_monitor_get_geometry(monitor, &frame);
if ((x >= frame.x && x <= frame.x + frame.width) &&
(y >= frame.y && y <= frame.y + frame.width)) {
monitor_with_window = monitor;
break;
}
}
fl_value_set_string_take(window_info, kScreenKey,
make_monitor_value(monitor_with_window));
gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(window));
fl_value_set_string_take(window_info, kScaleFactorKey,
fl_value_new_float(scale_factor));
return FL_METHOD_RESPONSE(fl_method_success_response_new(window_info));
}
// Sets the window position and dimensions.
static FlMethodResponse* set_window_frame(FlWindowSizePlugin* self,
FlValue* args) {
if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST ||
fl_value_get_length(args) != 4) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
kBadArgumentsError, "Expected 4-element list", nullptr));
}
double x = fl_value_get_float(fl_value_get_list_value(args, 0));
double y = fl_value_get_float(fl_value_get_list_value(args, 1));
double width = fl_value_get_float(fl_value_get_list_value(args, 2));
double height = fl_value_get_float(fl_value_get_list_value(args, 3));
GtkWindow* window = get_window(self);
if (window == nullptr) {
return FL_METHOD_RESPONSE(
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
}
gtk_window_move(window, static_cast<gint>(x), static_cast<gint>(y));
gtk_window_resize(window, static_cast<gint>(width),
static_cast<gint>(height));
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
}
// Send updated window geometry to GTK.
static void update_window_geometry(FlWindowSizePlugin* self) {
gtk_window_set_geometry_hints(
get_window(self), nullptr, &self->window_geometry,
static_cast<GdkWindowHints>(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE));
}
// Sets the window minimum size.
static FlMethodResponse* set_window_minimum_size(FlWindowSizePlugin* self,
FlValue* args) {
if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST ||
fl_value_get_length(args) != 2) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
kBadArgumentsError, "Expected 2-element list", nullptr));
}
double width = fl_value_get_float(fl_value_get_list_value(args, 0));
double height = fl_value_get_float(fl_value_get_list_value(args, 1));
if (get_window(self) == nullptr) {
return FL_METHOD_RESPONSE(
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
}
if (width >= 0 && height >= 0) {
self->window_geometry.min_width = static_cast<gint>(width);
self->window_geometry.min_height = static_cast<gint>(height);
}
update_window_geometry(self);
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
}
// Sets the window maximum size.
static FlMethodResponse* set_window_maximum_size(FlWindowSizePlugin* self,
FlValue* args) {
if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST ||
fl_value_get_length(args) != 2) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
kBadArgumentsError, "Expected 2-element list", nullptr));
}
double width = fl_value_get_float(fl_value_get_list_value(args, 0));
double height = fl_value_get_float(fl_value_get_list_value(args, 1));
if (get_window(self) == nullptr) {
return FL_METHOD_RESPONSE(
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
}
self->window_geometry.max_width = static_cast<gint>(width);
self->window_geometry.max_height = static_cast<gint>(height);
// Flutter uses -1 as unconstrained, GTK doesn't have an unconstrained value.
if (self->window_geometry.max_width < 0) {
self->window_geometry.max_width = G_MAXINT;
}
if (self->window_geometry.max_height < 0) {
self->window_geometry.max_height = G_MAXINT;
}
update_window_geometry(self);
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
}
// Sets the window title.
static FlMethodResponse* set_window_title(FlWindowSizePlugin* self,
FlValue* args) {
if (fl_value_get_type(args) != FL_VALUE_TYPE_STRING) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
kBadArgumentsError, "Expected string", nullptr));
}
GtkWindow* window = get_window(self);
if (window == nullptr) {
return FL_METHOD_RESPONSE(
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
}
gtk_window_set_title(window, fl_value_get_string(args));
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
}
// Sets the window visibility.
static FlMethodResponse* set_window_visible(FlWindowSizePlugin* self,
FlValue* args) {
if (fl_value_get_type(args) != FL_VALUE_TYPE_BOOL) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
kBadArgumentsError, "Expected bool", nullptr));
}
GtkWindow* window = get_window(self);
if (window == nullptr) {
return FL_METHOD_RESPONSE(
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
}
if (fl_value_get_bool(args)) {
gtk_widget_show(GTK_WIDGET(window));
} else {
gtk_widget_hide(GTK_WIDGET(window));
}
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
}
// Gets the window minimum size.
static FlMethodResponse* get_window_minimum_size(FlWindowSizePlugin* self) {
g_autoptr(FlValue) size = fl_value_new_list();
gint min_width = self->window_geometry.min_width;
gint min_height = self->window_geometry.min_height;
// GTK uses -1 for the requisition size (the size GTK has calculated).
// Report this as zero (smallest possible) so this doesn't look like Size(-1, -1).
if (min_width < 0) {
min_width = 0;
}
if (min_height < 0) {
min_height = 0;
}
fl_value_append_take(size, fl_value_new_float(min_width));
fl_value_append_take(size, fl_value_new_float(min_height));
return FL_METHOD_RESPONSE(fl_method_success_response_new(size));
}
// Gets the window maximum size.
static FlMethodResponse* get_window_maximum_size(FlWindowSizePlugin* self) {
g_autoptr(FlValue) size = fl_value_new_list();
gint max_width = self->window_geometry.max_width;
gint max_height = self->window_geometry.max_height;
// Flutter uses -1 as unconstrained, GTK doesn't have an unconstrained value.
if (max_width == G_MAXINT) {
max_width = -1;
}
if (max_height == G_MAXINT) {
max_height = -1;
}
fl_value_append_take(size, fl_value_new_float(max_width));
fl_value_append_take(size, fl_value_new_float(max_height));
return FL_METHOD_RESPONSE(fl_method_success_response_new(size));
}
// Called when a method call is received from Flutter.
static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call,
gpointer user_data) {
FlWindowSizePlugin* self = FL_WINDOW_SIZE_PLUGIN(user_data);
const gchar* method = fl_method_call_get_name(method_call);
FlValue* args = fl_method_call_get_args(method_call);
g_autoptr(FlMethodResponse) response = nullptr;
if (strcmp(method, kGetScreenListMethod) == 0) {
response = get_screen_list(self);
} else if (strcmp(method, kGetWindowInfoMethod) == 0) {
response = get_window_info(self);
} else if (strcmp(method, kSetWindowFrameMethod) == 0) {
response = set_window_frame(self, args);
} else if (strcmp(method, kSetWindowMinimumSizeMethod) == 0) {
response = set_window_minimum_size(self, args);
} else if (strcmp(method, kSetWindowMaximumSizeMethod) == 0) {
response = set_window_maximum_size(self, args);
} else if (strcmp(method, kSetWindowTitleMethod) == 0) {
response = set_window_title(self, args);
} else if (strcmp(method, ksetWindowVisibilityMethod) == 0) {
response = set_window_visible(self, args);
} else if (strcmp(method, kGetWindowMinimumSizeMethod) == 0) {
response = get_window_minimum_size(self);
} else if (strcmp(method, kGetWindowMaximumSizeMethod) == 0) {
response = get_window_maximum_size(self);
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
g_autoptr(GError) error = nullptr;
if (!fl_method_call_respond(method_call, response, &error))
g_warning("Failed to send method call response: %s", error->message);
}
static void fl_window_size_plugin_dispose(GObject* object) {
FlWindowSizePlugin* self = FL_WINDOW_SIZE_PLUGIN(object);
g_clear_object(&self->registrar);
g_clear_object(&self->channel);
G_OBJECT_CLASS(fl_window_size_plugin_parent_class)->dispose(object);
}
static void fl_window_size_plugin_class_init(FlWindowSizePluginClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_window_size_plugin_dispose;
}
static void fl_window_size_plugin_init(FlWindowSizePlugin* self) {
self->window_geometry.min_width = -1;
self->window_geometry.min_height = -1;
self->window_geometry.max_width = G_MAXINT;
self->window_geometry.max_height = G_MAXINT;
}
FlWindowSizePlugin* fl_window_size_plugin_new(FlPluginRegistrar* registrar) {
FlWindowSizePlugin* self = FL_WINDOW_SIZE_PLUGIN(
g_object_new(fl_window_size_plugin_get_type(), nullptr));
self->registrar = FL_PLUGIN_REGISTRAR(g_object_ref(registrar));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
self->channel =
fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),
kChannelName, FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(self->channel, method_call_cb,
g_object_ref(self), g_object_unref);
return self;
}
void window_size_plugin_register_with_registrar(FlPluginRegistrar* registrar) {
FlWindowSizePlugin* plugin = fl_window_size_plugin_new(registrar);
g_object_unref(plugin);
}

@ -1,20 +0,0 @@
name: window_size
description: Allows resizing and repositioning the window containing Flutter.
version: 0.1.0
# Do not publish this plugin. See:
# https://github.com/google/flutter-desktop-embedding/blob/master/plugins/README.md#using-plugins
publish_to: none
flutter:
plugin:
platforms:
linux:
pluginClass: WindowSizePlugin
environment:
sdk: '>=2.12.0-0 <3.0.0'
dependencies:
flutter:
sdk: flutter

@ -0,0 +1,14 @@
#!/usr/bin/env bash
# Downloads WASM locally and use local fonts
# Temporary solution until https://github.com/flutter/flutter/issues/70101 and 77580 provide a better way
flutter clean
flutter build web
wasmLocation=$(grep canvaskit-wasm build/web/main.dart.js | sed -e 's/.*https/https/' -e 's/\/bin.*/\/bin/' | uniq)
echo "Downloading WASM from $wasmLocation"
curl -o build/web/canvaskit.js "$wasmLocation/canvaskit.js"
curl -o build/web/canvaskit.wasm "$wasmLocation/canvaskit.wasm"
sed -i -e "s!$wasmLocation!.!" \
-e "s!https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf!./assets/packages/amos_mobile_widgets/assets/google_fonts/Roboto-Regular.ttf!" \
-e "s!https://fonts.googleapis.com/css2?family=Noto+Sans+Symbols!./assets/assets/css/Noto-Sans-Symbols.css!" \
-e "s!https://fonts.googleapis.com/css2?family=Noto+Color+Emoji+Compat!./assets/assets/css/Noto-Color-Emoji-Compat.css!" \
build/web/main.dart.js

@ -1,5 +1,7 @@
class ProviderModel {
const ProviderModel();
const ProviderModel({this.copyable = false});
final bool copyable;
}
class ProviderModelProp {

@ -12,9 +12,13 @@ class AnnotationField {
}
class ProviderGenerator extends GeneratorForAnnotation<ProviderModel> {
var _preClassName;
@override
generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep) {
var className = (element as ClassElement).source.shortName;
var needImports = className != _preClassName;
_preClassName = className;
List<AnnotationField> fields = [];
element.fields.forEach((field) {
var annotation = field.metadata.firstWhereOrNull(
@ -29,12 +33,57 @@ class ProviderGenerator extends GeneratorForAnnotation<ProviderModel> {
),
);
});
return '''
return [
_genImports(className, needImports),
_genClassDefine(element.displayName),
_genNamedConstructors(element.constructors, element.displayName),
_genCopyFunc(element.displayName, fields, annotation.read('copyable').boolValue),
_genSetPropsFunc(fields),
].whereNotNull();
}
}
String? _genImports(String className, bool needImports) => needImports
? '''
import 'package:flutter/foundation.dart';
import 'package:dde_gesture_manager/extensions/compare_extension.dart';
import '$className';
'''
: null;
String _genClassDefine(String displayName) => '''
class ${displayName}Provider extends $displayName with ChangeNotifier {
''';
String? _genNamedConstructors(List<ConstructorElement> constructors, String displayName) {
String _genCallSuperParamStr(ParameterElement param) => param.isNamed ? '${param.name}: ${param.name}' : param.name;
List<String> _constructors = [];
if (constructors.length > 0) {
constructors.forEach((constructor) {
if (constructor.name.length > 0) {
var params = constructor.getDisplayString(withNullability: true).split('$displayName.${constructor.name}').last;
_constructors.add('''
${displayName}Provider.${constructor.name}${params.replaceAll('dynamic ', '')}
: super.${constructor.name}(${constructor.parameters.map(_genCallSuperParamStr).join(',')});
''');
}
});
}
return _constructors.length > 0 ? _constructors.join('\n') : null;
}
String? _genCopyFunc(String displayName, List<AnnotationField> fields, bool copyable) {
if (!copyable) return null;
return '''
void copyFrom(${displayName} other) {
bool changed = false;
${fields.map((f) => 'if (other.${f.name}.diff(this.${f.name})) {this.${f.name} = other.${f.name}; changed = true; }').join('\n')}
if (changed) notifyListeners();
}
''';
}
class ${element.displayName}Provider extends ${element.displayName} with ChangeNotifier {
String _genSetPropsFunc(List<AnnotationField> fields) => '''
void setProps({
${fields.map((f) => '${f.type.endsWith('?') ? '' : 'required '}${f.type} ${f.name},').join('\n')}
}) {
@ -44,5 +93,3 @@ class ${element.displayName}Provider extends ${element.displayName} with ChangeN
}
}
''';
}
}

@ -0,0 +1,26 @@
import 'package:flutter/cupertino.dart';
/// [UOS](https://docs.uniontech.com/zh/content/t_dbG3kBK9iDf9B963ok)
const double localManagerPanelWidth = 260;
const double marketPanelWidth = 300;
const shortDuration = const Duration(milliseconds: 100);
const mediumDuration = const Duration(milliseconds: 300);
const longDuration = const Duration(milliseconds: 500);
const minWindowSize = const Size(800, 600);
const defaultWindowSize = const Size(1120, 720);
const double defaultBorderRadius = 8;
const double defaultButtonHeight = 36;
enum PanelType {
local_manager,
market,
}

@ -1,4 +1,4 @@
class SPKeys {
static final String brightnessMode = 'BRIGHTNESS_MODE';
static final String userLanguage = 'USER_LANGUAGE';
}
}

@ -6,6 +6,11 @@ extension SharedPreferencesExtenstion on SharedPreferences {
return this.setInt(key, value);
}
Future<bool> updateDouble(String key, double value) {
if (this.getDouble(key) == value) return Future.value(false);
return this.setDouble(key, value);
}
Future<bool> updateString(String key, String value) {
if (this.getString(key) == value) return Future.value(false);
return this.setString(key, value);

@ -5,6 +5,8 @@ extension SoutExtension on Object? {
return print(this);
case Null:
return print(null);
case List:
return print('[${(this as List).join(', ')}]');
default:
return print(this.toString());
}

@ -1,3 +1,4 @@
import 'package:dde_gesture_manager/constants/constants.dart';
import 'package:dde_gesture_manager/constants/sp_keys.dart';
import 'package:dde_gesture_manager/constants/supported_locales.dart';
import 'package:dde_gesture_manager/extensions.dart';
@ -54,6 +55,7 @@ class MyApp extends StatelessWidget {
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
home: AnimatedCrossFade(
crossFadeState: isDarkMode != null ? CrossFadeState.showSecond : CrossFadeState.showFirst,
alignment: Alignment.center,
@ -70,7 +72,7 @@ class MyApp extends StatelessWidget {
return Container();
}),
secondChild: HomePage(),
duration: Duration(milliseconds: 500),
duration: longDuration,
),
);
},

@ -0,0 +1,10 @@
import 'package:dde_gesture_manager/builder/provider_annotation.dart';
@ProviderModel()
class ContentLayout {
@ProviderModelProp()
bool? localManagerOpened;
@ProviderModelProp()
bool? marketOpened;
}

@ -0,0 +1,21 @@
export 'local_solutions_web.dart' if (dart.library.io) 'local_solutions_linux.dart';
import 'package:dde_gesture_manager/models/solution.dart';
abstract class LocalSolutionEntry {
Solution solution;
DateTime lastModifyTime;
String path;
LocalSolutionEntry({
required this.path,
required this.solution,
required this.lastModifyTime,
});
save();
}
abstract class LocalSolutionsInterface<T extends LocalSolutionEntry> {
Future<List<T>> get solutionEntries;
}

@ -0,0 +1,68 @@
import 'dart:io';
import 'package:dde_gesture_manager/builder/provider_annotation.dart';
import 'package:dde_gesture_manager/extensions.dart';
import 'package:dde_gesture_manager/models/solution.dart';
import 'package:path/path.dart' show join;
import 'package:path_provider/path_provider.dart';
import 'local_solutions.dart';
export 'local_solutions.dart';
@ProviderModel()
class LocalSolutions implements LocalSolutionsInterface<LocalSolutionEntryLinux> {
LocalSolutions() {
solutionEntries.then((value) => solutions = value);
}
@override
Future<List<LocalSolutionEntryLinux>> get solutionEntries async {
var _supportDirectory = await getApplicationSupportDirectory();
var directory = Directory(join(_supportDirectory.path, 'solutions'));
if (!directory.existsSync()) directory.createSync();
directory.path.sout();
return directory
.list()
.map<LocalSolutionEntryLinux?>((f) {
LocalSolutionEntryLinux? entry;
try {
var content = File(f.path).readAsStringSync();
entry = LocalSolutionEntryLinux(
path: f.path, solution: Solution.parse(content), lastModifyTime: f.statSync().modified);
} catch (e) {
e.sout();
}
return entry;
})
.where((e) => e != null)
.cast<LocalSolutionEntryLinux>()
.toList();
}
@ProviderModelProp()
List<LocalSolutionEntry>? solutions;
}
class LocalSolutionEntryLinux implements LocalSolutionEntry {
@override
String path;
@override
Solution solution;
@override
DateTime lastModifyTime;
LocalSolutionEntryLinux({
required this.path,
required this.solution,
required this.lastModifyTime,
});
@override
save() {
// TODO: implement save
throw UnimplementedError();
}
}

@ -0,0 +1 @@
export 'local_solutions_web.provider.dart' if (dart.library.io) 'local_solutions_linux.provider.dart';

@ -0,0 +1,67 @@
import 'dart:convert';
import 'package:dde_gesture_manager/builder/provider_annotation.dart';
import 'package:dde_gesture_manager/models/solution.dart';
import 'package:dde_gesture_manager/extensions.dart';
import 'dart:html';
import 'local_solutions.dart';
export 'local_solutions.dart';
@ProviderModel()
class LocalSolutions implements LocalSolutionsInterface<LocalSolutionEntryWeb> {
LocalSolutions() {
solutionEntries.then((value) => solutions = value);
}
@override
Future<List<LocalSolutionEntryWeb>> get solutionEntries async {
return window.localStorage.keys
.map<LocalSolutionEntryWeb?>((key) {
if (key.startsWith('solutions.')) {
LocalSolutionEntryWeb? entry;
try {
var content = window.localStorage[key] ?? '';
var solutionJson = json.decode(content);
entry = LocalSolutionEntryWeb(
path: key,
solution: Solution.parse(solutionJson),
lastModifyTime: DateTime.parse(solutionJson['modified_at']),
);
} catch (e) {
e.sout();
}
return entry;
}
})
.where((e) => e != null)
.cast<LocalSolutionEntryWeb>()
.toList();
}
@ProviderModelProp()
List<LocalSolutionEntry>? solutions;
}
class LocalSolutionEntryWeb implements LocalSolutionEntry {
@override
String path;
@override
Solution solution;
@override
DateTime lastModifyTime;
LocalSolutionEntryWeb({
required this.path,
required this.solution,
required this.lastModifyTime,
});
@override
save() {
// TODO: implement save
throw UnimplementedError();
}
}

@ -0,0 +1,92 @@
import 'dart:convert';
import 'package:dde_gesture_manager/builder/provider_annotation.dart';
import 'package:dde_gesture_manager/utils/helper.dart';
@ProviderModel(copyable: true)
class Solution {
@ProviderModelProp()
String? name;
@ProviderModelProp()
String? description;
@ProviderModelProp()
List<GestureProp>? gestures;
Solution.parse(solution) {
if (solution is String) solution = json.decode(solution);
assert(solution is Map);
name = solution['name'];
description = solution['desc'];
gestures = (solution['gestures'] as List? ?? []).map<GestureProp>((ele) => GestureProp.parse(ele)).toList();
}
}
enum Gesture {
swipe,
tap,
pinch,
}
enum GestureDirection {
up,
down,
left,
right,
pinch_in,
pinch_out,
none,
}
enum GestureType {
built_in,
commandline,
shortcut,
}
@ProviderModel(copyable: true)
class GestureProp {
@ProviderModelProp()
Gesture? gesture;
@ProviderModelProp()
GestureDirection? direction;
@ProviderModelProp()
int? fingers;
@ProviderModelProp()
GestureType? type;
@ProviderModelProp()
String? command;
@ProviderModelProp()
String? remark;
@override
bool operator ==(Object other) =>
other is GestureProp &&
other.gesture == this.gesture &&
other.direction == this.direction &&
other.fingers == this.fingers;
@override
String toString() {
return 'GestureProp{gesture: $gesture, direction: $direction, fingers: $fingers, type: $type, command: $command, remark: $remark}';
}
GestureProp.empty();
GestureProp.parse(props) {
if (props is String) props = json.decode(props);
assert(props is Map);
gesture = H.getGestureByName(props['gesture']);
direction = H.getGestureDirectionByName(props['direction']);
fingers = props['fingers'];
type = H.getGestureTypeByName(props['type']);
command = props['command'];
remark = props['remark'];
}
}

@ -0,0 +1,51 @@
import 'package:dde_gesture_manager/extensions.dart';
import 'package:dde_gesture_manager/pages/gesture_editor.dart';
import 'package:dde_gesture_manager/pages/local_manager.dart';
import 'package:dde_gesture_manager/pages/market.dart';
import 'package:flutter/material.dart';
import 'package:dde_gesture_manager/models/content_layout.provider.dart';
import 'package:dde_gesture_manager/utils/helper.dart';
class Content extends StatefulWidget {
const Content({Key? key}) : super(key: key);
@override
_ContentState createState() => _ContentState();
}
class _ContentState extends State<Content> {
double? preWindowWidth;
@override
Widget build(BuildContext context) {
var windowWidth = MediaQuery.of(context).size.width;
var preferredPanelsStatus = H.getPreferredPanelsStatus(windowWidth);
var widthChanged = preWindowWidth != null && preWindowWidth != windowWidth;
var widget = ChangeNotifierProvider(
create: (context) => ContentLayoutProvider()
..localManagerOpened = preferredPanelsStatus.localManagerPanelOpened
..marketOpened = preferredPanelsStatus.marketPanelOpened,
builder: (context, child) {
if (widthChanged && mounted) {
Future.microtask(
() => context.read<ContentLayoutProvider>().setProps(
localManagerOpened: preferredPanelsStatus.localManagerPanelOpened,
marketOpened: preferredPanelsStatus.marketPanelOpened,
),
);
}
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocalManager(),
GestureEditor(),
Market(),
],
);
},
);
preWindowWidth = windowWidth;
return widget;
}
}

@ -1,4 +1,5 @@
import 'package:dde_gesture_manager/extensions.dart';
import 'package:dde_gesture_manager/widgets/help_button.dart';
import 'package:dde_gesture_manager/widgets/language_switcher.dart';
import 'package:dde_gesture_manager/widgets/theme_switcher.dart';
import 'package:dde_gesture_manager/widgets/version_checker.dart';
@ -27,9 +28,9 @@ class _FooterState extends State<Footer> {
Row(
children: [
LanguageSwitcher(),
Container(width: 6),
ThemeSwitcher(),
],
HelpButton(),
].map((e) => Padding(padding: EdgeInsets.only(right: 6), child: e)).toList(),
)
],
),

@ -0,0 +1,169 @@
import 'package:dde_gesture_manager/constants/constants.dart';
import 'package:dde_gesture_manager/extensions.dart';
import 'package:dde_gesture_manager/models/content_layout.provider.dart';
import 'package:dde_gesture_manager/models/solution.dart';
import 'package:dde_gesture_manager/models/solution.provider.dart';
import 'package:dde_gesture_manager/utils/helper.dart';
import 'package:dde_gesture_manager/widgets/dde_button.dart';
import 'package:dde_gesture_manager/widgets/dde_data_table.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class GestureEditor extends StatelessWidget {
const GestureEditor({Key? key}) : super(key: key);
List<DDataRow> _buildDataRow(List<GestureProp>? gestures, BuildContext context) => (gestures ?? [])
.map((gesture) => DDataRow(
onSelectChanged: (selected) {
if (selected == true)
context.read<GesturePropProvider>().setProps(
gesture: gesture.gesture,
direction: gesture.direction,
fingers: gesture.fingers,
type: gesture.type,
command: gesture.command,
remark: gesture.remark,
);
},
selected: context.watch<GesturePropProvider>() == gesture,
cells: [
Center(
child: Text('${LocaleKeys.gesture_editor_gestures}.${H.getGestureName(gesture.gesture)}').tr(),
),
Center(
child: Text('${LocaleKeys.gesture_editor_directions}.${H.getGestureDirectionName(gesture.direction)}')
.tr()),
Center(
child: Text('${gesture.fingers}'),
),
Center(child: Text('${LocaleKeys.gesture_editor_types}.${H.getGestureTypeName(gesture.type)}').tr()),
Text(gesture.command ?? ''),
Text(gesture.remark ?? ''),
]
.map(
(ele) => DDataCell(ele),
)
.toList(),
))
.toList();
@override
Widget build(BuildContext context) {
var layoutProvider = context.watch<ContentLayoutProvider>();
var solutionProvider = context.watch<SolutionProvider>();
return Flexible(
child: Padding(
padding: const EdgeInsets.all(10),
child: Container(
decoration: BoxDecoration(
color: context.t.backgroundColor,
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Visibility(
visible: layoutProvider.localManagerOpened == false,
child: DButton(
width: defaultButtonHeight,
onTap: () => H.openPanel(context, PanelType.local_manager),
child: Icon(
CupertinoIcons.square_list,
),
),
),
Text(
LocaleKeys.gesture_editor_label,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
),
).tr(),
Visibility(
visible: layoutProvider.marketOpened == false,
child: DButton(
width: defaultButtonHeight,
onTap: () => H.openPanel(context, PanelType.market),
child: Icon(
CupertinoIcons.cart,
),
),
),
],
),
Container(height: 10),
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(defaultBorderRadius),
border: Border.all(
width: .2,
color: context.t.dividerColor,
),
),
width: double.infinity,
clipBehavior: Clip.antiAlias,
child: LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
return Scrollbar(
isAlwaysShown: true,
child: SingleChildScrollView(
primary: true,
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: constraints.maxWidth),
child: DDataTable(
showCheckboxColumn: true,
headerBackgroundColor: context.t.dialogBackgroundColor,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(defaultBorderRadius),
border: Border.all(
width: .2,
color: context.t.dividerColor,
),
),
dataRowColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) return context.t.dialogBackgroundColor;
if (states.contains(MaterialState.selected)) return Colors.blueAccent;
return null;
}),
columns: [
DDataColumn(label: Text(LocaleKeys.gesture_editor_gesture.tr()), center: true),
DDataColumn(label: Text(LocaleKeys.gesture_editor_direction.tr()), center: true),
DDataColumn(label: Text(LocaleKeys.gesture_editor_fingers.tr()), center: true),
DDataColumn(label: Text(LocaleKeys.gesture_editor_type.tr()), center: true),
DDataColumn(label: Text(LocaleKeys.gesture_editor_command.tr())),
DDataColumn(label: Text(LocaleKeys.gesture_editor_remark.tr())),
],
rows: _buildDataRow(solutionProvider.gestures, context),
),
),
),
);
}),
),
),
Container(height: 10),
Container(
height: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(defaultBorderRadius),
border: Border.all(
width: .2,
color: context.t.dividerColor,
),
),
),
],
),
),
),
),
);
}
}

@ -1,4 +1,8 @@
import 'package:dde_gesture_manager/widgets/footer.dart';
import 'package:dde_gesture_manager/extensions.dart';
import 'package:dde_gesture_manager/models/local_solutions_provider.dart';
import 'package:dde_gesture_manager/models/solution.provider.dart';
import 'package:dde_gesture_manager/pages/content.dart';
import 'package:dde_gesture_manager/pages/footer.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@ -12,21 +16,71 @@ class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Text('WIP'),
],
),
SizedBox(
height: 36,
child: Footer(),
),
body: MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => SolutionProvider.parse('''
{
"name": "test",
"desc": "some desc",
"gestures": [
{
"gesture": "swipe",
"direction": "down",
"fingers": 3,
"type": "shortcut",
"command": "ctrl+w",
"remark": "close current page."
},
{
"gesture": "swipe",
"direction": "up",
"fingers": 3,
"type": "shortcut",
"command": "ctrl+alt+t",
"remark": "reopen last closed page."
},
{
"gesture": "pinch",
"direction": "in",
"fingers": 4,
"type": "shortcut",
"command": "ctrl+alt+f",
"remark": "search files."
},
{
"gesture": "tap",
"fingers": 4,
"type": "built_in",
"command": "handle4FingersTap",
"remark": "handle4FingersTap."
},
{
"gesture": "swipe",
"direction": "down",
"fingers": 5,
"type": "commandline",
"command": "dbus-send --type=method_call --dest=com.deepin.dde.Launcher /com/deepin/dde/Launcher com.deepin.dde.Launcher.Toggle",
"remark": "toggle launcher."
}
]
}
''')),
ChangeNotifierProvider(create: (context) => GesturePropProvider.empty()),
ChangeNotifierProvider(create: (context) => LocalSolutionsProvider(),lazy: false),
],
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Content(),
),
SizedBox(
height: 36,
child: Footer(),
),
],
),
),
);
}

@ -0,0 +1,143 @@
import 'package:dde_gesture_manager/constants/constants.dart';
import 'package:dde_gesture_manager/extensions.dart';
import 'package:dde_gesture_manager/models/content_layout.provider.dart';
import 'package:dde_gesture_manager/models/local_solutions_provider.dart';
import 'package:dde_gesture_manager/models/solution.provider.dart';
import 'package:dde_gesture_manager/widgets/dde_button.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
class LocalManager extends StatefulWidget {
const LocalManager({
Key? key,
}) : super(key: key);
@override
State<LocalManager> createState() => _LocalManagerState();
}
class _LocalManagerState extends State<LocalManager> {
late ScrollController _scrollController;
int? _hoveringIndex;
int? _selectedIndex;
@override
void initState() {
super.initState();
_scrollController = ScrollController();
}
Color _getItemBackgroundColor(int index) {
Color _color = index % 2 == 0 ? context.t.scaffoldBackgroundColor : context.t.backgroundColor;
if (index == _hoveringIndex) _color = context.t.scaffoldBackgroundColor;
if (index == _selectedIndex) _color = Colors.blueAccent;
return _color;
}
@override
Widget build(BuildContext context) {
var isOpen = context.watch<ContentLayoutProvider>().localManagerOpened == true;
var localSolutions = context.watch<LocalSolutionsProvider>().solutions ?? [];
return AnimatedContainer(
duration: mediumDuration,
curve: Curves.easeInOut,
width: isOpen ? localManagerPanelWidth : 0,
child: OverflowBox(
alignment: Alignment.centerRight,
maxWidth: localManagerPanelWidth,
minWidth: localManagerPanelWidth,
child: Material(
color: context.t.backgroundColor,
elevation: isOpen ? 10 : 0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(width: defaultButtonHeight),
Flexible(
child: Center(
child: Text(
LocaleKeys.local_manager_title,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
),
).tr(),
),
),
DButton(
width: defaultButtonHeight - 2,
height: defaultButtonHeight - 2,
onTap: () => context.read<ContentLayoutProvider>().setProps(localManagerOpened: !isOpen),
child: Icon(
CupertinoIcons.chevron_left_2,
size: 20,
),
),
],
),
Flexible(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: ListView.builder(
controller: _scrollController,
itemBuilder: (context, index) => GestureDetector(
onDoubleTap: () {
context.read<SolutionProvider>().copyFrom(localSolutions[index].solution);
setState(() {
_selectedIndex = index;
});
},
onTap: () {
setState(() {
_selectedIndex = index;
});
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (_) {
setState(() {
_hoveringIndex = index;
});
},
child: Container(
color: _getItemBackgroundColor(index),
child: Padding(
padding: const EdgeInsets.only(right: 12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(localSolutions[index].solution.name ?? ''),
Text('456'),
],
),
),
),
),
),
itemCount: localSolutions.length,
),
),
Container(
height: 150,
color: Colors.black,
),
],
),
),
],
),
),
),
),
);
}
}

@ -0,0 +1,65 @@
import 'package:dde_gesture_manager/constants/constants.dart';
import 'package:dde_gesture_manager/extensions.dart';
import 'package:dde_gesture_manager/models/content_layout.provider.dart';
import 'package:dde_gesture_manager/widgets/dde_button.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Market extends StatelessWidget {
const Market({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
var isOpen = context.watch<ContentLayoutProvider>().marketOpened == true;
return AnimatedContainer(
duration: mediumDuration,
curve: Curves.easeInOut,
width: isOpen ? marketPanelWidth : 0,
child: OverflowBox(
alignment: Alignment.centerLeft,
maxWidth: marketPanelWidth,
minWidth: marketPanelWidth,
child: Material(
color: context.t.backgroundColor,
elevation: isOpen ? 10 : 0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DButton(
width: defaultButtonHeight - 2,
height: defaultButtonHeight - 2,
onTap: () => context.read<ContentLayoutProvider>().setProps(marketOpened: !isOpen),
child: Icon(
CupertinoIcons.chevron_right_2,
size: 20,
),
),
Flexible(
child: Center(
child: Text(
LocaleKeys.market_title,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
),
).tr(),
),
),
Container(width: defaultButtonHeight),
],
),
],
),
),
),
),
);
}
}

@ -1,5 +1,26 @@
import 'package:flutter/material.dart';
import 'package:dde_gesture_manager/constants/constants.dart';
var darkTheme = ThemeData.dark().copyWith(
primaryColor: Colors.grey,
scaffoldBackgroundColor: Color(0xff252525),
backgroundColor: Color(0xff282828),
iconTheme: IconThemeData(
color: Color(0xffc0c6d4),
),
dividerColor: Color(0xfff3f3f3),
textTheme: ThemeData.dark().textTheme.copyWith(
headline1: TextStyle(
color: Color(0xffc0c6d4),
),
bodyText2: TextStyle(
color: Color(0xffc0c6d4),
),
),
popupMenuTheme: ThemeData.dark().popupMenuTheme.copyWith(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(defaultBorderRadius),
),
),
dialogBackgroundColor: Color(0xff202020),
);

@ -1,5 +1,26 @@
import 'package:dde_gesture_manager/constants/constants.dart';
import 'package:flutter/material.dart';
var lightTheme = ThemeData.light().copyWith(
primaryColor: Colors.blue,
);
scaffoldBackgroundColor: Color(0xfff8f8f8),
backgroundColor: Color(0xffffffff),
iconTheme: IconThemeData(
color: Color(0xff414d68),
),
dividerColor: Color(0xfff3f3f3),
textTheme: ThemeData.light().textTheme.copyWith(
headline1: TextStyle(
color: Color(0xff414d68),
),
bodyText2: TextStyle(
color: Color(0xff414d68),
),
),
popupMenuTheme: ThemeData.dark().popupMenuTheme.copyWith(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(defaultBorderRadius),
),
),
dialogBackgroundColor: Color(0xfffefefe),
);

@ -1,4 +1,9 @@
import 'package:dde_gesture_manager/models/content_layout.provider.dart';
import 'package:dde_gesture_manager/models/solution.dart';
import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:dde_gesture_manager/constants/constants.dart';
import 'package:dde_gesture_manager/extensions.dart';
class H {
H._();
@ -14,4 +19,97 @@ class H {
initSharedPreference() async {
_sp = await SharedPreferences.getInstance();
}
static void openPanel(BuildContext context, PanelType panelType) {
var windowWidth = MediaQuery.of(context).size.width;
if (windowWidth < minWindowSize.width + localManagerPanelWidth + marketPanelWidth) {
context.read<ContentLayoutProvider>().setProps(
localManagerOpened: panelType == PanelType.local_manager,
marketOpened: panelType == PanelType.market,
);
} else {
switch (panelType) {
case PanelType.local_manager:
return context.read<ContentLayoutProvider>().setProps(localManagerOpened: true);
case PanelType.market:
return context.read<ContentLayoutProvider>().setProps(marketOpened: true);
}
}
}
static PreferredPanelsStatus getPreferredPanelsStatus(double windowWidth) {
var preferredPanelsStatus = PreferredPanelsStatus(localManagerPanelOpened: true, marketPanelOpened: true);
if (windowWidth > minWindowSize.width + localManagerPanelWidth + marketPanelWidth)
return preferredPanelsStatus;
else if (windowWidth < minWindowSize.width + localManagerPanelWidth)
return preferredPanelsStatus
..marketPanelOpened = false
..localManagerPanelOpened = false;
else
return preferredPanelsStatus..marketPanelOpened = false;
}
static String? getGestureName(Gesture? gesture) => const {
Gesture.swipe: 'swipe',
Gesture.tap: 'tap',
Gesture.pinch: 'pinch',
}[gesture];
static Gesture getGestureByName(String gestureName) =>
const {
'swipe': Gesture.swipe,
'tap': Gesture.tap,
'pinch': Gesture.pinch,
}[gestureName] ??
Gesture.swipe;
static String? getGestureDirectionName(GestureDirection? direction) => const {
GestureDirection.up: 'up',
GestureDirection.down: 'down',
GestureDirection.left: 'left',
GestureDirection.right: 'right',
GestureDirection.pinch_in: 'in',
GestureDirection.pinch_out: 'out',
GestureDirection.none: 'none',
}[direction];
static GestureDirection getGestureDirectionByName(String? directionName) =>
const {
'up': GestureDirection.up,
'down': GestureDirection.down,
'left': GestureDirection.left,
'right': GestureDirection.right,
'in': GestureDirection.pinch_in,
'out': GestureDirection.pinch_out,
}[directionName] ??
GestureDirection.none;
static String? getGestureTypeName(GestureType? type) => const {
GestureType.built_in: 'built_in',
GestureType.shortcut: 'shortcut',
GestureType.commandline: 'commandline',
}[type];
static GestureType getGestureTypeByName(String typeName) =>
const {
'built_in': GestureType.built_in,
'shortcut': GestureType.shortcut,
'commandline': GestureType.commandline,
}[typeName] ??
GestureType.built_in;
}
class PreferredPanelsStatus {
bool localManagerPanelOpened;
bool marketPanelOpened;
PreferredPanelsStatus({
required this.localManagerPanelOpened,
required this.marketPanelOpened,
});
@override
String toString() {
return 'PreferredPanelsStatus{localManagerPanelOpened: $localManagerPanelOpened, marketPanelOpened: $marketPanelOpened}';
}
}

@ -1,3 +1,4 @@
import 'package:dde_gesture_manager/constants/constants.dart';
import 'package:dde_gesture_manager/constants/sp_keys.dart';
import 'package:dde_gesture_manager/constants/supported_locales.dart';
import 'package:dde_gesture_manager/extensions.dart';
@ -40,7 +41,7 @@ Future<void> initConfigs() async {
var userLanguageIndex = H().sp.getInt(SPKeys.userLanguage) ?? 0;
var locale = supportedLocales[userLanguageIndex];
windowManager.setTitle(CodegenLoader.mapLocales[locale.toString()]?[LocaleKeys.app_name]);
windowManager.setMinimumSize(const Size(800, 600));
windowManager.setMinimumSize(minWindowSize);
}
var windowManager = WindowManager.instance;

@ -0,0 +1,54 @@
import 'package:dde_gesture_manager/constants/constants.dart';
import 'package:flutter/material.dart';
import 'package:glass_kit/glass_kit.dart';
class DButton extends StatefulWidget {
final double width;
final double height;
final Widget child;
final GestureTapCallback? onTap;
const DButton({
Key? key,
required this.width,
this.height = defaultButtonHeight,
required this.child,
this.onTap,
}) : super(key: key);
@override
State<DButton> createState() => _DButtonState();
}
class _DButtonState extends State<DButton> {
bool _hovering = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onTap,
child: GlassContainer(
width: widget.width,
height: widget.height,
gradient: LinearGradient(
colors: [Colors.white.withOpacity(_hovering ? 0.1 : 0.15), Colors.white.withOpacity(0.1)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderColor: Color(0xff565656),
borderWidth: 1,
borderRadius: BorderRadius.circular(defaultBorderRadius),
child: MouseRegion(
onEnter: (event) => setState(() {
_hovering = true;
}),
onExit: (event) => setState(() {
_hovering = false;
}),
cursor: SystemMouseCursors.click,
child: Center(child: widget.child),
),
),
);
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,28 @@
import 'package:dde_gesture_manager/extensions.dart';
import 'package:flutter/material.dart';
class HelpButton extends StatelessWidget {
const HelpButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {},
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: Tooltip(
message: LocaleKeys.help_tip.tr(),
child: Row(
children: [
Icon(Icons.help_outline, size: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 3),
child: Text(LocaleKeys.help_label).tr(),
),
],
),
),
),
);
}
}

@ -1,3 +1,4 @@
import 'package:collection/collection.dart';
import 'package:dde_gesture_manager/constants/sp_keys.dart';
import 'package:dde_gesture_manager/constants/supported_locales.dart';
import 'package:dde_gesture_manager/extensions.dart';
@ -6,7 +7,6 @@ import 'package:dde_gesture_manager/utils/helper.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:window_manager/window_manager.dart';
class LanguageSwitcher extends StatelessWidget {
@ -18,6 +18,7 @@ class LanguageSwitcher extends StatelessWidget {
var _supportedLocale = supportedLocales.firstWhereOrNull((element) => element == _locale);
return PopupMenuButton<SupportedLocale>(
tooltip: LocaleKeys.language_tip.tr(),
child: Row(
children: [
Icon(Icons.language_outlined, size: 20),

@ -1,9 +1,9 @@
import 'package:dde_gesture_manager/extensions.dart';
import 'package:dde_gesture_manager/generated/locale_keys.g.dart';
import 'package:dde_gesture_manager/models/configs.dart';
import 'package:dde_gesture_manager/models/configs.provider.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:dde_gesture_manager/extensions.dart';
class ThemeSwitcher extends StatelessWidget {
const ThemeSwitcher({Key? key}) : super(key: key);
@ -23,7 +23,7 @@ class ThemeSwitcher extends StatelessWidget {
],
),
padding: EdgeInsets.zero,
tooltip: LocaleKeys.theme_label.tr(),
tooltip: LocaleKeys.theme_tip.tr(),
itemBuilder: (BuildContext context) => [
PopupMenuItem<BrightnessMode>(
child: ListTile(

@ -7,13 +7,9 @@
#include "generated_plugin_registrant.h"
#include <window_manager/window_manager_plugin.h>
#include <window_size/window_size_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) window_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
window_manager_plugin_register_with_registrar(window_manager_registrar);
g_autoptr(FlPluginRegistrar) window_size_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowSizePlugin");
window_size_plugin_register_with_registrar(window_size_registrar);
}

@ -4,7 +4,6 @@
list(APPEND FLUTTER_PLUGIN_LIST
window_manager
window_size
)
set(PLUGIN_BUNDLED_LIBRARIES)

@ -48,7 +48,7 @@ static void my_application_activate(GApplication* application) {
gtk_window_set_title(window, "");
}
gtk_window_set_default_size(window, 800, 600);
gtk_window_set_default_size(window, 1120, 720);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();

@ -31,11 +31,12 @@ dependencies:
gsettings: 0.2.0
provider: ^6.0.0
package_info_plus: ^1.0.6
window_size:
path: 3rd_party/window_size
easy_localization: ^3.0.0
glass_kit: ^2.0.1
rect_getter: ^1.0.0
path_provider: ^2.0.5
xdg_directories_web:
path: 3rd_party/xdg_directories_web
easy_localization: ^3.0.0
dev_dependencies:
flutter_test:

@ -3,7 +3,8 @@
"label": "Theme",
"dark": "Dark",
"light": "Light",
"system": "System"
"system": "System",
"tip": "Choose a theme"
},
"version": {
"current": "current version",
@ -11,6 +12,51 @@
},
"app_name": "Gesture Manager for DDE",
"language": {
"label": "Language"
"label": "Language",
"tip": "Choose a language"
},
"help": {
"label": "Help",
"tip": "Display help documentation"
},
"market": {
"title": "Solution market"
},
"local_manager": {
"title": "Local solution management"
},
"gesture_editor": {
"label": "Gesture program editing",
"gesture": "gesture",
"direction": "direction",
"fingers": "fingers",
"type": "type",
"command": "command",
"remark": "remark",
"directions": {
"up": "up",
"down": "down",
"left": "left",
"right": "right",
"in": "pinch-in",
"out": "pinch-out",
"none": "/"
},
"gestures": {
"swipe": "swipe",
"pinch": "pinch",
"tap": "pinch"
},
"types": {
"built_in": "built-in",
"commandline": "commandline",
"shortcut": "shortcut"
}
},
"operation": {
"add": "Add",
"delete": "delete",
"duplicate": "duplicate",
"apply": "apply"
}
}

@ -3,7 +3,8 @@
"label": "主题",
"dark": "深色",
"light": "浅色",
"system": "跟随系统"
"system": "跟随系统",
"tip": "选择界面主题"
},
"version": {
"current": "当前版本",
@ -11,6 +12,51 @@
},
"app_name": "DDE手势管理器",
"language": {
"label": "语言"
"label": "语言",
"tip": "选择语言"
},
"help": {
"label": "帮助",
"tip": "显示帮助文档"
},
"market": {
"title": "方案市场"
},
"local_manager": {
"title": "本地方案管理"
},
"gesture_editor": {
"label": "手势方案编辑",
"gesture": "手势",
"direction": "方向",
"fingers": "手指数",
"type": "类型",
"command": "命令",
"remark": "备注",
"directions": {
"up": "向上",
"down": "向下",
"left": "向左",
"right": "向右",
"in": "向内捏合",
"out": "向外展开",
"none": "/"
},
"gestures": {
"swipe": "划",
"pinch": "捏",
"tap": "点"
},
"types": {
"built_in": "内置操作",
"commandline": "命令行",
"shortcut": "快捷键"
}
},
"operation": {
"add": "新增",
"delete": "删除",
"duplicate": "复制",
"apply": "应用"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 917 B

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Loading…
Cancel
Save