Upgrade Flutter
This commit is contained in:
parent
a9393134b8
commit
ca278c046a
12
.metadata
12
.metadata
|
|
@ -4,7 +4,7 @@
|
|||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
||||
revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
|
|
@ -13,11 +13,11 @@ project_type: app
|
|||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
||||
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
||||
- platform: macos
|
||||
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
||||
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
|
||||
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||
- platform: linux
|
||||
create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||
base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b
|
||||
|
||||
# User provided section
|
||||
|
||||
|
|
|
|||
|
|
@ -153,9 +153,12 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
|||
try {
|
||||
authenticated = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Please authenticate to access the app',
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
useErrorDialogs: true,
|
||||
stickyAuth: false);
|
||||
stickyAuth: false,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
|
@ -384,6 +387,11 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
|||
? LockScreen(onAuthenticatePressed: _authenticate)
|
||||
: InitScreen(),
|
||||
locale: locale,
|
||||
/*
|
||||
theme: state.prefState.enableDarkMode
|
||||
? ThemeData.dark(useMaterial3: true)
|
||||
: ThemeData.light(useMaterial3: true),
|
||||
*/
|
||||
theme: state.prefState.enableDarkMode
|
||||
? ThemeData(
|
||||
colorScheme: ColorScheme.dark().copyWith(
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import 'package:image_cropper/image_cropper.dart';
|
|||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:share/share.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
// Project imports:
|
||||
|
|
@ -227,7 +227,7 @@ class DocumentTile extends StatelessWidget {
|
|||
|
||||
await File(filePath)
|
||||
.writeAsBytes(response.bodyBytes);
|
||||
await Share.shareFiles([filePath]);
|
||||
await Share.shareXFiles([XFile(filePath)]);
|
||||
}
|
||||
} else if (value == localization.delete) {
|
||||
confirmCallback(
|
||||
|
|
|
|||
|
|
@ -1400,7 +1400,7 @@ void _showAbout(BuildContext context) async {
|
|||
padding: const EdgeInsets.only(top: 4),
|
||||
child: AppButton(
|
||||
label: localization.appPlatforms.toUpperCase(),
|
||||
iconData: MdiIcons.desktopMac,
|
||||
iconData: MdiIcons.desktopClassic,
|
||||
onPressed: () {
|
||||
showDialog<AlertDialog>(
|
||||
context: context,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import 'package:invoiceninja_flutter/ui/app/forms/date_picker.dart';
|
|||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:printing/printing.dart';
|
||||
import 'package:share/share.dart';
|
||||
|
||||
// Project imports:
|
||||
import 'package:invoiceninja_flutter/data/models/dashboard_model.dart';
|
||||
|
|
@ -40,6 +39,7 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
|
|||
|
||||
import 'package:invoiceninja_flutter/utils/web_stub.dart'
|
||||
if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
class ClientPdfView extends StatefulWidget {
|
||||
const ClientPdfView({
|
||||
|
|
@ -404,7 +404,7 @@ class _ClientPdfViewState extends State<ClientPdfView> {
|
|||
showToast(
|
||||
localization.fileSavedInDownloadsFolder);
|
||||
} else {
|
||||
await Share.shareFiles([filePath]);
|
||||
await Share.shareXFiles([XFile(filePath)]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -439,6 +439,7 @@ class _ClientPdfViewState extends State<ClientPdfView> {
|
|||
callback: (_) => loadPDF(sendEmail: true));
|
||||
},
|
||||
),
|
||||
if (supportsSchedules())
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
if (!state.isProPlan) {
|
||||
|
|
@ -452,8 +453,8 @@ class _ClientPdfViewState extends State<ClientPdfView> {
|
|||
section: kSettingsAccountManagement));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child:
|
||||
Text(localization.upgrade.toUpperCase())),
|
||||
child: Text(
|
||||
localization.upgrade.toUpperCase())),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
|
@ -461,7 +462,8 @@ class _ClientPdfViewState extends State<ClientPdfView> {
|
|||
createEntity(
|
||||
context: context,
|
||||
entity: ScheduleEntity().rebuild((b) => b
|
||||
..template = ScheduleEntity.TEMPLATE_EMAIL_STATEMENT
|
||||
..template =
|
||||
ScheduleEntity.TEMPLATE_EMAIL_STATEMENT
|
||||
..parameters.clients.add(client.id)
|
||||
..parameters.showAgingTable = _showAging
|
||||
..parameters.showPaymentsTable = _showPayments
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import 'package:invoiceninja_flutter/main_app.dart';
|
|||
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:printing/printing.dart';
|
||||
import 'package:share/share.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
// Project imports:
|
||||
|
|
@ -282,7 +282,7 @@ class _InvoicePdfViewState extends State<InvoicePdfView> {
|
|||
showToast(localization
|
||||
.fileSavedInDownloadsFolder);
|
||||
} else {
|
||||
await Share.shareFiles([filePath]);
|
||||
await Share.shareXFiles([XFile(filePath)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
|
|||
import 'package:memoize/memoize.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:share/share.dart';
|
||||
|
||||
// Project imports:
|
||||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
|
|
@ -51,6 +50,7 @@ import 'package:invoiceninja_flutter/utils/formatting.dart';
|
|||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/money.dart';
|
||||
import 'package:invoiceninja_flutter/utils/strings.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'credit_report.dart';
|
||||
|
||||
import 'package:invoiceninja_flutter/utils/web_stub.dart'
|
||||
|
|
@ -515,7 +515,7 @@ class ReportsScreenVM {
|
|||
if (isDesktopOS()) {
|
||||
showToast(localization.fileSavedInDownloadsFolder);
|
||||
} else {
|
||||
await Share.shareFiles([filePath]);
|
||||
await Share.shareXFiles([XFile(filePath)]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -152,9 +152,10 @@ class DeviceSettingsVM {
|
|||
authenticated = await LocalAuthentication().authenticate(
|
||||
localizedReason:
|
||||
AppLocalization.of(context).authenticateToChangeSetting,
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
useErrorDialogs: true,
|
||||
stickyAuth: false);
|
||||
stickyAuth: false));
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ class _SettingsListState extends State<SettingsList> {
|
|||
),
|
||||
*/
|
||||
if (showAll) ...[
|
||||
if (supportsSchedules())
|
||||
SettingsListTile(
|
||||
section: kSettingsSchedules,
|
||||
viewModel: widget.viewModel,
|
||||
|
|
@ -623,6 +624,7 @@ class SettingsSearch extends StatelessWidget {
|
|||
'subscriptions',
|
||||
],
|
||||
],
|
||||
if (supportsSchedules())
|
||||
kSettingsSchedules: [
|
||||
[
|
||||
'schedules#2023-02-15',
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ bool supportsAppleOAuth() => kIsWeb || isApple();
|
|||
// TODO remove this function
|
||||
bool supportsMicrosoftOAuth() => kIsWeb;
|
||||
|
||||
// TODO remove this
|
||||
bool supportsSchedules() {
|
||||
final store = StoreProvider.of<AppState>(navigatorKey.currentContext);
|
||||
return store.state.isSelfHosted;
|
||||
}
|
||||
|
||||
bool supportsInAppPurchase() {
|
||||
final store = StoreProvider.of<AppState>(navigatorKey.currentContext);
|
||||
if (store.state.isSelfHosted) {
|
||||
|
|
|
|||
|
|
@ -34,12 +34,20 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
|
||||
ScrollController _scrollController;
|
||||
|
||||
final _darkBackground = const Color(0xFF222222);
|
||||
final _lightBackground = Colors.white;
|
||||
Brightness _brightness = Brightness.light;
|
||||
|
||||
SuperEditorDebugVisualsConfig _debugConfig;
|
||||
|
||||
OverlayEntry _textFormatBarOverlayEntry;
|
||||
final _textSelectionAnchor = ValueNotifier<Offset>(null);
|
||||
|
||||
OverlayEntry _imageFormatBarOverlayEntry;
|
||||
final _imageSelectionAnchor = ValueNotifier<Offset>(null);
|
||||
|
||||
final _overlayController = MagnifierAndToolbarController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -55,8 +63,10 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
_doc = deserializeMarkdownToDocument(markdown)
|
||||
..addListener(_hideOrShowToolbar)
|
||||
..addListener(_onChanged);
|
||||
|
||||
_docEditor = DocumentEditor(document: _doc as MutableDocument);
|
||||
_composer = DocumentComposer()..addListener(_hideOrShowToolbar);
|
||||
_composer = DocumentComposer();
|
||||
_composer.selectionNotifier.addListener(_hideOrShowToolbar);
|
||||
_docOps = CommonEditorOperations(
|
||||
editor: _docEditor,
|
||||
composer: _composer,
|
||||
|
|
@ -169,15 +179,16 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
|
||||
void _showEditorToolbar() {
|
||||
if (_textFormatBarOverlayEntry == null) {
|
||||
// Create an overlay entry to build the editor toolbar.
|
||||
// TODO: add an overlay to the Editor widget to avoid using the
|
||||
// application overlay
|
||||
_textFormatBarOverlayEntry ??= OverlayEntry(builder: (context) {
|
||||
return Theme(
|
||||
data: ThemeData.light(),
|
||||
child: EditorToolbar(
|
||||
return EditorToolbar(
|
||||
anchor: _textSelectionAnchor,
|
||||
editorFocusNode: _editorFocusNode,
|
||||
editor: _docEditor,
|
||||
composer: _composer,
|
||||
closeToolbar: _hideEditorToolbar,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -199,7 +210,6 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
_composer.selection.base, _composer.selection.extent);
|
||||
final docBox =
|
||||
_docLayoutKey.currentContext.findRenderObject() as RenderBox;
|
||||
|
||||
final overlayBoundingBox = Rect.fromPoints(
|
||||
docBox.localToGlobal(docBoundingBox.topLeft),
|
||||
docBox.localToGlobal(docBoundingBox.bottomRight),
|
||||
|
|
@ -222,7 +232,6 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
// and non-null implies the entry is in the overlay.
|
||||
_textFormatBarOverlayEntry.remove();
|
||||
_textFormatBarOverlayEntry = null;
|
||||
}
|
||||
|
||||
// Ensure that focus returns to the editor.
|
||||
//
|
||||
|
|
@ -231,6 +240,7 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
// editor. I'm not sure why.
|
||||
_editorFocusNode.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
DocumentGestureMode get _gestureMode {
|
||||
switch (defaultTargetPlatform) {
|
||||
|
|
@ -245,29 +255,41 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
return DocumentGestureMode.mouse;
|
||||
}
|
||||
|
||||
return null;
|
||||
return DocumentGestureMode.mouse;
|
||||
}
|
||||
|
||||
bool get _isMobile => _gestureMode != DocumentGestureMode.mouse;
|
||||
|
||||
DocumentInputSource get _inputSource {
|
||||
TextInputSource get _inputSource {
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.iOS:
|
||||
return DocumentInputSource.ime;
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.macOS:
|
||||
case TargetPlatform.windows:
|
||||
return DocumentInputSource.keyboard;
|
||||
return TextInputSource.ime;
|
||||
// return DocumentInputSource.keyboard;
|
||||
}
|
||||
|
||||
return null;
|
||||
return TextInputSource.ime;
|
||||
}
|
||||
|
||||
void _cut() {
|
||||
_docOps.cut();
|
||||
_overlayController.hideToolbar();
|
||||
}
|
||||
|
||||
void _copy() {
|
||||
_docOps.copy();
|
||||
_overlayController.hideToolbar();
|
||||
}
|
||||
|
||||
void _paste() {
|
||||
_docOps.paste();
|
||||
_overlayController.hideToolbar();
|
||||
}
|
||||
|
||||
void _cut() => _docOps.cut();
|
||||
void _copy() => _docOps.copy();
|
||||
void _paste() => _docOps.paste();
|
||||
void _selectAll() => _docOps.selectAll();
|
||||
|
||||
void _showImageToolbar() {
|
||||
|
|
@ -277,7 +299,7 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
return ImageFormatToolbar(
|
||||
anchor: _imageSelectionAnchor,
|
||||
composer: _composer,
|
||||
setWidth: (dynamic nodeId, dynamic width) {
|
||||
setWidth: (nodeId, width) {
|
||||
final node = _doc.getNodeById(nodeId);
|
||||
final currentStyles =
|
||||
SingleColumnLayoutComponentStyles.fromMetadata(node);
|
||||
|
|
@ -309,10 +331,8 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
final docBox =
|
||||
_docLayoutKey.currentContext.findRenderObject() as RenderBox;
|
||||
final overlayBoundingBox = Rect.fromPoints(
|
||||
docBox.localToGlobal(docBoundingBox.topLeft,
|
||||
ancestor: context.findRenderObject()),
|
||||
docBox.localToGlobal(docBoundingBox.bottomRight,
|
||||
ancestor: context.findRenderObject()),
|
||||
docBox.localToGlobal(docBoundingBox.topLeft),
|
||||
docBox.localToGlobal(docBoundingBox.bottomRight),
|
||||
);
|
||||
|
||||
_imageSelectionAnchor.value = overlayBoundingBox.center;
|
||||
|
|
@ -332,36 +352,139 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
// and non-null implies the entry is in the overlay.
|
||||
_imageFormatBarOverlayEntry.remove();
|
||||
_imageFormatBarOverlayEntry = null;
|
||||
}
|
||||
|
||||
// Ensure that focus returns to the editor.
|
||||
_editorFocusNode.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
return Theme(
|
||||
data: ThemeData(brightness: _brightness),
|
||||
child: Builder(builder: (themedContext) {
|
||||
// This builder captures the new theme
|
||||
return Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditor(),
|
||||
child: _buildEditor(themedContext),
|
||||
),
|
||||
if (_isMobile) _buildMountedToolbar(),
|
||||
],
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: _buildCornerFabs(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEditor() {
|
||||
return SuperEditor(
|
||||
Widget _buildCornerFabs() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 16, bottom: 16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_buildDebugVisualsToggle(),
|
||||
const SizedBox(height: 16),
|
||||
_buildLightAndDarkModeToggle(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDebugVisualsToggle() {
|
||||
return FloatingActionButton(
|
||||
backgroundColor:
|
||||
_brightness == Brightness.light ? _darkBackground : _lightBackground,
|
||||
foregroundColor:
|
||||
_brightness == Brightness.light ? _lightBackground : _darkBackground,
|
||||
elevation: 5,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_debugConfig = _debugConfig != null
|
||||
? null
|
||||
: SuperEditorDebugVisualsConfig(
|
||||
showFocus: true,
|
||||
showImeConnection: true,
|
||||
);
|
||||
});
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.bug_report,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLightAndDarkModeToggle() {
|
||||
return FloatingActionButton(
|
||||
backgroundColor:
|
||||
_brightness == Brightness.light ? _darkBackground : _lightBackground,
|
||||
foregroundColor:
|
||||
_brightness == Brightness.light ? _lightBackground : _darkBackground,
|
||||
elevation: 5,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_brightness = _brightness == Brightness.light
|
||||
? Brightness.dark
|
||||
: Brightness.light;
|
||||
});
|
||||
},
|
||||
child: _brightness == Brightness.light
|
||||
? const Icon(
|
||||
Icons.dark_mode,
|
||||
)
|
||||
: const Icon(
|
||||
Icons.light_mode,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEditor(BuildContext context) {
|
||||
final isLight = Theme.of(context).brightness == Brightness.light;
|
||||
|
||||
return ColoredBox(
|
||||
color: isLight ? _lightBackground : _darkBackground,
|
||||
child: SuperEditorDebugVisuals(
|
||||
config: _debugConfig ?? const SuperEditorDebugVisualsConfig(),
|
||||
child: SuperEditor(
|
||||
editor: _docEditor,
|
||||
composer: _composer,
|
||||
focusNode: _editorFocusNode,
|
||||
scrollController: _scrollController,
|
||||
documentLayoutKey: _docLayoutKey,
|
||||
documentOverlayBuilders: [
|
||||
DefaultCaretOverlayBuilder(
|
||||
CaretStyle()
|
||||
.copyWith(color: isLight ? Colors.black : Colors.redAccent),
|
||||
),
|
||||
],
|
||||
selectionStyle: isLight
|
||||
? defaultSelectionStyle
|
||||
: SelectionStyles(
|
||||
selectionColor: Colors.red.withOpacity(0.3),
|
||||
),
|
||||
stylesheet: defaultStylesheet.copyWith(
|
||||
addRulesAfter: [
|
||||
if (!isLight) ..._darkModeStyles,
|
||||
taskStyles,
|
||||
],
|
||||
),
|
||||
componentBuilders: [
|
||||
TaskComponentBuilder(_docEditor),
|
||||
...defaultComponentBuilders,
|
||||
],
|
||||
gestureMode: _gestureMode,
|
||||
inputSource: _inputSource,
|
||||
keyboardActions: _inputSource == TextInputSource.ime
|
||||
? defaultImeKeyboardActions
|
||||
: defaultKeyboardActions,
|
||||
androidToolbarBuilder: (_) => AndroidTextEditingFloatingToolbar(
|
||||
onCutPressed: _cut,
|
||||
onCopyPressed: _copy,
|
||||
|
|
@ -373,6 +496,9 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
onCopyPressed: _copy,
|
||||
onPastePressed: _paste,
|
||||
),
|
||||
overlayController: _overlayController,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -398,3 +524,37 @@ class _ExampleEditorState extends State<ExampleEditor> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Makes text light, for use during dark mode styling.
|
||||
final _darkModeStyles = [
|
||||
StyleRule(
|
||||
BlockSelector.all,
|
||||
(doc, docNode) {
|
||||
return <String, dynamic>{
|
||||
'textStyle': const TextStyle(
|
||||
color: Color(0xFFCCCCCC),
|
||||
),
|
||||
};
|
||||
},
|
||||
),
|
||||
StyleRule(
|
||||
const BlockSelector("header1"),
|
||||
(doc, docNode) {
|
||||
return <String, dynamic>{
|
||||
'textStyle': const TextStyle(
|
||||
color: Color(0xFF888888),
|
||||
),
|
||||
};
|
||||
},
|
||||
),
|
||||
StyleRule(
|
||||
const BlockSelector("header2"),
|
||||
(doc, docNode) {
|
||||
return <String, dynamic>{
|
||||
'textStyle': const TextStyle(
|
||||
color: Color(0xFF888888),
|
||||
),
|
||||
};
|
||||
},
|
||||
),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class EditorToolbar extends StatefulWidget {
|
|||
const EditorToolbar({
|
||||
Key key,
|
||||
@required this.anchor,
|
||||
@required this.editorFocusNode,
|
||||
@required this.editor,
|
||||
@required this.composer,
|
||||
@required this.closeToolbar,
|
||||
|
|
@ -26,6 +27,9 @@ class EditorToolbar extends StatefulWidget {
|
|||
/// reposition itself as the [Offset] value changes.
|
||||
final ValueNotifier<Offset> anchor;
|
||||
|
||||
/// The [FocusNode] attached to the editor to which this toolbar applies.
|
||||
final FocusNode editorFocusNode;
|
||||
|
||||
/// The [editor] is used to alter document content, such as
|
||||
/// when the user selects a different block format for a
|
||||
/// text blob, e.g., paragraph, header, blockquote, or
|
||||
|
|
@ -49,13 +53,13 @@ class EditorToolbar extends StatefulWidget {
|
|||
class _EditorToolbarState extends State<EditorToolbar> {
|
||||
bool _showUrlField = false;
|
||||
FocusNode _urlFocusNode;
|
||||
TextEditingController _urlController;
|
||||
AttributedTextEditingController _urlController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_urlFocusNode = FocusNode();
|
||||
_urlController = TextEditingController();
|
||||
_urlController = SingleLineAttributedTextEditingController(_applyLink);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -352,7 +356,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
|||
/// Takes the text from the [urlController] and applies it as a link
|
||||
/// attribution to the currently selected text.
|
||||
void _applyLink() {
|
||||
final url = _urlController.text;
|
||||
final url = _urlController.text.text;
|
||||
|
||||
final selection = widget.composer.selection;
|
||||
final baseOffset = (selection.base.nodePosition as TextPosition).offset;
|
||||
|
|
@ -360,7 +364,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
|||
final selectionStart = min(baseOffset, extentOffset);
|
||||
final selectionEnd = max(baseOffset, extentOffset);
|
||||
final selectionRange =
|
||||
SpanRange(start: selectionStart, end: selectionEnd - 1);
|
||||
TextRange(start: selectionStart, end: selectionEnd - 1);
|
||||
|
||||
final textNode =
|
||||
widget.editor.document.getNodeById(selection.extent.nodeId) as TextNode;
|
||||
|
|
@ -387,7 +391,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
|||
/// Given [text] and a [range] within the [text], the [range] is
|
||||
/// shortened on both sides to remove any trailing whitespace and
|
||||
/// the new range is returned.
|
||||
SpanRange _trimTextRangeWhitespace(AttributedText text, SpanRange range) {
|
||||
SpanRange _trimTextRangeWhitespace(AttributedText text, TextRange range) {
|
||||
int startOffset = range.start;
|
||||
int endOffset = range.end;
|
||||
|
||||
|
|
@ -504,6 +508,9 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
|||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
/*
|
||||
// https://github.com/superlistapp/super_editor/issues/689
|
||||
// https://github.com/flutter/flutter/issues/106923
|
||||
// Only allow the user to select a new type of text node if
|
||||
// the currently selected node can be converted.
|
||||
if (_isConvertibleNode()) ...[
|
||||
|
|
@ -512,7 +519,6 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
|||
child: DropdownButton<_TextType>(
|
||||
value: _getCurrentTextType(),
|
||||
items: _TextType.values
|
||||
.where((element) => element != _TextType.blockquote)
|
||||
.map((textType) => DropdownMenuItem<_TextType>(
|
||||
value: textType,
|
||||
child: Padding(
|
||||
|
|
@ -522,8 +528,10 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
|||
))
|
||||
.toList(),
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
fontSize: 12,
|
||||
),
|
||||
underline: const SizedBox(),
|
||||
|
|
@ -534,6 +542,7 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
|||
),
|
||||
_buildVerticalDivider(),
|
||||
],
|
||||
*/
|
||||
Center(
|
||||
child: IconButton(
|
||||
onPressed: _toggleBold,
|
||||
|
|
@ -604,6 +613,15 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
|||
),
|
||||
),
|
||||
],
|
||||
_buildVerticalDivider(),
|
||||
Center(
|
||||
child: IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.more_vert),
|
||||
splashRadius: 16,
|
||||
tooltip: 'More Options',
|
||||
),
|
||||
),
|
||||
*/
|
||||
],
|
||||
),
|
||||
|
|
@ -623,14 +641,37 @@ class _EditorToolbarState extends State<EditorToolbar> {
|
|||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
child: FocusWithCustomParent(
|
||||
focusNode: _urlFocusNode,
|
||||
controller: _urlController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'enter url...',
|
||||
border: InputBorder.none,
|
||||
parentFocusNode: widget.editorFocusNode,
|
||||
// We use a SuperTextField instead of a TextField because TextField
|
||||
// automatically re-parents its FocusNode, which causes #609. Flutter
|
||||
// #106923 tracks the TextField issue.
|
||||
child: SuperTextField(
|
||||
focusNode: _urlFocusNode,
|
||||
textController: _urlController,
|
||||
minLines: 1,
|
||||
maxLines: 1,
|
||||
inputSource: TextInputSource.ime,
|
||||
hintBehavior: HintBehavior.displayHintUntilTextEntered,
|
||||
hintBuilder: (context) {
|
||||
return Text(
|
||||
'Enter a url...',
|
||||
style: const TextStyle(
|
||||
color: Colors.grey,
|
||||
fontSize: 16,
|
||||
),
|
||||
);
|
||||
},
|
||||
textStyleBuilder: (_) {
|
||||
return TextStyle(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
fontSize: 16,
|
||||
);
|
||||
},
|
||||
),
|
||||
onSubmitted: (newValue) => _applyLink(),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
|
|
@ -840,3 +881,21 @@ class _PositionedToolbar extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SingleLineAttributedTextEditingController
|
||||
extends AttributedTextEditingController {
|
||||
SingleLineAttributedTextEditingController(this.onSubmit);
|
||||
|
||||
final VoidCallback onSubmit;
|
||||
|
||||
@override
|
||||
void insertNewline() {
|
||||
// Don't insert newline in a single-line text field.
|
||||
|
||||
// Invoke callback to take action on enter.
|
||||
onSubmit();
|
||||
|
||||
// TODO: this is a hack. SuperTextField shouldn't insert newlines in a single
|
||||
// line field (#697).
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@ import Foundation
|
|||
import in_app_purchase_storekit
|
||||
import in_app_review
|
||||
import package_info
|
||||
import package_info_plus_macos
|
||||
import path_provider_macos
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import printing
|
||||
import screen_retriever
|
||||
import sentry_flutter
|
||||
import shared_preferences_macos
|
||||
import share_plus
|
||||
import shared_preferences_foundation
|
||||
import sign_in_with_apple
|
||||
import smart_auth
|
||||
import sqflite
|
||||
|
|
@ -29,6 +30,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin"))
|
||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))
|
||||
SmartAuthPlugin.register(with: registry.registrar(forPlugin: "SmartAuthPlugin"))
|
||||
|
|
|
|||
|
|
@ -20,16 +20,16 @@ dependencies:
|
|||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
# google_sign_in: ^5.0.7
|
||||
# google_sign_in: ^6.0.1
|
||||
# in_app_review: ^2.0.4
|
||||
# in_app_purchase: ^3.1.1
|
||||
# pinput: ^2.2.11
|
||||
flutter_redux: ^0.8.2
|
||||
flutter_redux: ^0.10.0
|
||||
redux_logging: ^0.5.0
|
||||
http: ^0.13.3
|
||||
path_provider: ^2.0.2
|
||||
shared_preferences: ^2.0.6
|
||||
material_design_icons_flutter: ^5.0.6295
|
||||
material_design_icons_flutter: ^6.0.7096
|
||||
built_value: ^8.1.2
|
||||
built_collection: ^5.1.0
|
||||
memoize: ^3.0.0
|
||||
|
|
@ -37,46 +37,46 @@ dependencies:
|
|||
url_launcher: ^6.0.20
|
||||
share: ^2.0.4
|
||||
intl: ^0.17.0
|
||||
flutter_slidable: ^1.1.0
|
||||
flutter_slidable: ^2.0.0
|
||||
charts_flutter: ^0.12.0
|
||||
#qr_flutter: ^4.0.0
|
||||
qr_flutter: # https://github.com/theyakka/qr.flutter/issues/174#issuecomment-1084235757
|
||||
git:
|
||||
url: https://github.com/theyakka/qr.flutter.git
|
||||
local_auth: ^1.1.6
|
||||
sentry_flutter: ^6.5.1
|
||||
image_picker: ^0.8.3+1
|
||||
local_auth: ^2.1.5
|
||||
sentry_flutter: ^6.20.1
|
||||
image_picker: ^0.8.6+3
|
||||
flutter_colorpicker: ^1.0.3
|
||||
flutter_json_viewer: ^1.0.1
|
||||
webview_flutter: ^3.0.4
|
||||
timeago: ^3.1.0
|
||||
package_info: ^2.0.2
|
||||
rounded_loading_button: ^2.0.5
|
||||
version: ^2.0.0
|
||||
flutter_launcher_icons: ^0.9.1
|
||||
rounded_loading_button: ^2.1.0
|
||||
version: ^3.0.2
|
||||
# flutter_launcher_icons: ^0.9.1
|
||||
overflow_view: ^0.3.1
|
||||
flutter_styled_toast: ^2.0.0
|
||||
permission_handler: ^9.2.0
|
||||
file_picker: ^4.2.3
|
||||
permission_handler: ^10.2.0
|
||||
file_picker: ^5.2.5
|
||||
boardview: ^0.2.2
|
||||
pointer_interceptor: ^0.9.0
|
||||
contacts_service: ^0.6.1
|
||||
contacts_service: ^0.6.3
|
||||
diacritic: ^0.1.3
|
||||
states_rebuilder: ^5.2.0
|
||||
#super_editor: ^0.2.2
|
||||
states_rebuilder: ^6.2.0
|
||||
# super_editor: ^0.2.2
|
||||
markdown: ^5.0.0 # REMOVE THIS
|
||||
super_editor:
|
||||
git:
|
||||
url: https://github.com/superlistapp/super_editor.git
|
||||
path: super_editor
|
||||
ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
||||
html2md: ^1.2.5
|
||||
printing: ^5.8.0
|
||||
image_cropper: ^2.0.2
|
||||
html2md: ^1.2.6
|
||||
printing: ^5.10.1
|
||||
image_cropper: ^3.0.1
|
||||
msal_js: ^2.14.0
|
||||
sign_in_with_apple: ^4.0.0
|
||||
window_manager: ^0.2.7
|
||||
# bitsdojo_window: ^0.1.2
|
||||
sign_in_with_apple: ^4.3.0
|
||||
window_manager: ^0.3.0
|
||||
# bitsdojo_window: ^0.1.5
|
||||
intl_phone_field: ^3.1.0
|
||||
flutter_staggered_grid_view: ^0.6.2
|
||||
# quick_actions: ^0.2.1
|
||||
|
|
|
|||
1095
pubspec.lock
1095
pubspec.lock
File diff suppressed because it is too large
Load Diff
|
|
@ -44,39 +44,39 @@ dependencies:
|
|||
git:
|
||||
url: https://github.com/theyakka/qr.flutter.git
|
||||
local_auth: ^1.1.6
|
||||
sentry_flutter: ^6.5.1
|
||||
image_picker: ^0.8.3+1
|
||||
sentry_flutter: ^6.20.1
|
||||
image_picker: ^0.8.6+3
|
||||
flutter_colorpicker: ^1.0.3
|
||||
flutter_json_viewer: ^1.0.1
|
||||
webview_flutter: ^2.0.10
|
||||
timeago: ^3.1.0
|
||||
package_info: ^2.0.2
|
||||
rounded_loading_button: ^2.0.5
|
||||
rounded_loading_button: ^2.1.0
|
||||
version: ^2.0.0
|
||||
flutter_launcher_icons: ^0.9.1
|
||||
# flutter_launcher_icons: ^0.9.1
|
||||
overflow_view: ^0.3.1
|
||||
flutter_styled_toast: ^2.0.0
|
||||
permission_handler: ^9.2.0
|
||||
file_picker: ^4.2.3
|
||||
file_picker: ^5.2.5
|
||||
boardview: ^0.2.2
|
||||
pointer_interceptor: ^0.9.0
|
||||
contacts_service: ^0.6.1
|
||||
contacts_service: ^0.6.3
|
||||
diacritic: ^0.1.3
|
||||
states_rebuilder: ^5.2.0
|
||||
#super_editor: ^0.2.0
|
||||
# super_editor: ^0.2.0
|
||||
markdown: ^5.0.0 # REMOVE THIS
|
||||
super_editor:
|
||||
git:
|
||||
url: https://github.com/superlistapp/super_editor.git
|
||||
path: super_editor
|
||||
ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
||||
html2md: ^1.2.5
|
||||
printing: ^5.8.0
|
||||
image_cropper: ^2.0.2
|
||||
html2md: ^1.2.6
|
||||
printing: ^5.10.1
|
||||
image_cropper: ^3.0.1
|
||||
msal_js: ^2.14.0
|
||||
sign_in_with_apple: ^4.0.0
|
||||
window_manager: ^0.2.5
|
||||
bitsdojo_window: ^0.1.2
|
||||
sign_in_with_apple: ^4.3.0
|
||||
window_manager: ^0.3.0
|
||||
# bitsdojo_window: ^0.1.5
|
||||
intl_phone_field: ^3.1.0
|
||||
flutter_staggered_grid_view: ^0.6.2
|
||||
# quick_actions: ^0.2.1
|
||||
|
|
|
|||
46
pubspec.yaml
46
pubspec.yaml
|
|
@ -20,7 +20,7 @@ dependencies:
|
|||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
google_sign_in: ^5.0.7
|
||||
google_sign_in: ^6.0.1
|
||||
in_app_review: ^2.0.4
|
||||
in_app_purchase: ^3.1.1
|
||||
pinput: ^2.2.11
|
||||
|
|
@ -29,59 +29,59 @@ dependencies:
|
|||
# git:
|
||||
# url: https://github.com/Tkko/Flutter_Pinput
|
||||
# ref: dev
|
||||
flutter_redux: ^0.8.2
|
||||
flutter_redux: ^0.10.0
|
||||
redux_logging: ^0.5.0
|
||||
http: ^0.13.3
|
||||
path_provider: ^2.0.2
|
||||
shared_preferences: ^2.0.6
|
||||
material_design_icons_flutter: ^5.0.6295
|
||||
material_design_icons_flutter: ^6.0.7096
|
||||
built_value: ^8.1.2
|
||||
built_collection: ^5.1.0
|
||||
memoize: ^3.0.0
|
||||
cached_network_image: 3.0.0 # imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
|
||||
url_launcher: ^6.0.20
|
||||
share: ^2.0.4
|
||||
share_plus: ^6.3.1
|
||||
intl: ^0.17.0
|
||||
flutter_slidable: ^1.1.0
|
||||
flutter_slidable: ^2.0.0
|
||||
charts_flutter: ^0.12.0
|
||||
#qr_flutter: ^4.0.0
|
||||
qr_flutter: # https://github.com/theyakka/qr.flutter/issues/174#issuecomment-1084235757
|
||||
git:
|
||||
url: https://github.com/theyakka/qr.flutter.git
|
||||
local_auth: ^1.1.6
|
||||
sentry_flutter: ^6.9.1
|
||||
image_picker: ^0.8.3+1
|
||||
local_auth: ^2.1.5
|
||||
sentry_flutter: ^6.20.1
|
||||
image_picker: ^0.8.6+3
|
||||
flutter_colorpicker: ^1.0.3
|
||||
flutter_json_viewer: ^1.0.1
|
||||
webview_flutter: ^3.0.4
|
||||
timeago: ^3.1.0
|
||||
package_info: ^2.0.2
|
||||
rounded_loading_button: ^2.0.5
|
||||
version: ^2.0.0
|
||||
flutter_launcher_icons: ^0.9.1
|
||||
rounded_loading_button: ^2.1.0
|
||||
version: ^3.0.2
|
||||
# flutter_launcher_icons: ^0.9.1
|
||||
overflow_view: ^0.3.1
|
||||
flutter_styled_toast: ^2.0.0
|
||||
permission_handler: ^9.2.0
|
||||
file_picker: ^4.2.3
|
||||
permission_handler: ^10.2.0
|
||||
file_picker: ^5.2.5
|
||||
boardview: ^0.2.2
|
||||
pointer_interceptor: ^0.9.0
|
||||
contacts_service: ^0.6.1
|
||||
contacts_service: ^0.6.3
|
||||
diacritic: ^0.1.3
|
||||
states_rebuilder: ^5.2.0
|
||||
#super_editor: ^0.2.2
|
||||
states_rebuilder: ^6.2.0
|
||||
# super_editor: ^0.2.2
|
||||
markdown: ^5.0.0 # REMOVE THIS
|
||||
super_editor:
|
||||
git:
|
||||
url: https://github.com/superlistapp/super_editor.git
|
||||
path: super_editor
|
||||
ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
||||
html2md: ^1.2.5
|
||||
printing: ^5.8.0
|
||||
image_cropper: ^2.0.2
|
||||
#ref: 1601fdce95ebfa34bddf80cab3e58e700b0edec3
|
||||
html2md: ^1.2.6
|
||||
printing: ^5.10.1
|
||||
image_cropper: ^3.0.1
|
||||
msal_js: ^2.14.0
|
||||
sign_in_with_apple: ^4.0.0
|
||||
window_manager: ^0.2.7
|
||||
# bitsdojo_window: ^0.1.2
|
||||
sign_in_with_apple: ^4.3.0
|
||||
window_manager: ^0.3.0
|
||||
# bitsdojo_window: ^0.1.5
|
||||
intl_phone_field: ^3.1.0
|
||||
flutter_staggered_grid_view: ^0.6.2
|
||||
# quick_actions: ^0.2.1
|
||||
|
|
|
|||
|
|
@ -6,15 +6,19 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <local_auth_windows/local_auth_plugin.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <printing/printing_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <sentry_flutter/sentry_flutter_plugin.h>
|
||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||
#include <smart_auth/smart_auth_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
LocalAuthPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
PrintingPluginRegisterWithRegistrar(
|
||||
|
|
@ -23,6 +27,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||
SentryFlutterPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SentryFlutterPlugin"));
|
||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||
SmartAuthPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SmartAuthPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
local_auth_windows
|
||||
permission_handler_windows
|
||||
printing
|
||||
screen_retriever
|
||||
sentry_flutter
|
||||
share_plus
|
||||
smart_auth
|
||||
url_launcher_windows
|
||||
window_manager
|
||||
|
|
|
|||
|
|
@ -20,12 +20,20 @@ add_executable(${BINARY_NAME} WIN32
|
|||
# that need different build settings.
|
||||
apply_standard_settings(${BINARY_NAME})
|
||||
|
||||
# Add preprocessor definitions for the build version.
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
|
||||
|
||||
# Disable Windows macros that collide with C++ standard library functions.
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
|
||||
|
||||
# Add dependency libraries and include directories. Add any application-specific
|
||||
# dependencies here.
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
|
||||
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
# Run the Flutter tool portions of the build. This must not be removed.
|
||||
|
|
|
|||
|
|
@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico"
|
|||
// Version
|
||||
//
|
||||
|
||||
#ifdef FLUTTER_BUILD_NUMBER
|
||||
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
|
||||
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
|
||||
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
|
||||
#else
|
||||
#define VERSION_AS_NUMBER 1,0,0
|
||||
#define VERSION_AS_NUMBER 1,0,0,0
|
||||
#endif
|
||||
|
||||
#ifdef FLUTTER_BUILD_NAME
|
||||
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
|
||||
#if defined(FLUTTER_VERSION)
|
||||
#define VERSION_AS_STRING FLUTTER_VERSION
|
||||
#else
|
||||
#define VERSION_AS_STRING "1.0.0"
|
||||
#endif
|
||||
|
|
@ -93,7 +93,7 @@ BEGIN
|
|||
VALUE "FileDescription", "Invoice Ninja" "\0"
|
||||
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
||||
VALUE "InternalName", "invoiceninja" "\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2021 Invoice Ninja. All rights reserved." "\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2023 Invoice Ninja. All rights reserved." "\0"
|
||||
VALUE "OriginalFilename", "invoiceninja.exe" "\0"
|
||||
VALUE "ProductName", "invoiceninja" "\0"
|
||||
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ bool FlutterWindow::OnCreate() {
|
|||
}
|
||||
RegisterPlugins(flutter_controller_->engine());
|
||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||
|
||||
flutter_controller_->engine()->SetNextFrameCallback([&]() {
|
||||
this->Show();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
//#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
|
||||
//auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP);
|
||||
|
||||
#include <flutter/dart_project.h>
|
||||
#include <flutter/flutter_view_controller.h>
|
||||
#include <windows.h>
|
||||
|
|
@ -30,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
|||
FlutterWindow window(project);
|
||||
Win32Window::Point origin(10, 10);
|
||||
Win32Window::Size size(1280, 720);
|
||||
if (!window.CreateAndShow(L"Invoice Ninja", origin, size)) {
|
||||
if (!window.Create(L"Invoice Ninja", origin, size)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
window.SetQuitOnClose(true);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
</application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 -->
|
||||
<!-- Windows 10 and Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,31 @@
|
|||
#include "win32_window.h"
|
||||
|
||||
#include <dwmapi.h>
|
||||
#include <flutter_windows.h>
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
namespace {
|
||||
|
||||
/// Window attribute that enables dark mode window decorations.
|
||||
///
|
||||
/// Redefined in case the developer's machine has a Windows SDK older than
|
||||
/// version 10.0.22000.0.
|
||||
/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
#endif
|
||||
|
||||
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
|
||||
|
||||
/// Registry key for app theme preference.
|
||||
///
|
||||
/// A value of 0 indicates apps should use dark mode. A non-zero or missing
|
||||
/// value indicates apps should use light mode.
|
||||
constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
|
||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
|
||||
constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
|
||||
|
||||
// The number of Win32Window objects that currently exist.
|
||||
static int g_active_window_count = 0;
|
||||
|
||||
|
|
@ -31,8 +49,8 @@ void EnableFullDpiSupportIfAvailable(HWND hwnd) {
|
|||
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
|
||||
if (enable_non_client_dpi_scaling != nullptr) {
|
||||
enable_non_client_dpi_scaling(hwnd);
|
||||
FreeLibrary(user32_module);
|
||||
}
|
||||
FreeLibrary(user32_module);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -102,7 +120,7 @@ Win32Window::~Win32Window() {
|
|||
Destroy();
|
||||
}
|
||||
|
||||
bool Win32Window::CreateAndShow(const std::wstring& title,
|
||||
bool Win32Window::Create(const std::wstring& title,
|
||||
const Point& origin,
|
||||
const Size& size) {
|
||||
Destroy();
|
||||
|
|
@ -117,7 +135,7 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
|
|||
double scale_factor = dpi / 96.0;
|
||||
|
||||
HWND window = CreateWindow(
|
||||
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||
window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
|
||||
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
|
||||
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
||||
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
||||
|
|
@ -126,9 +144,15 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
|
|||
return false;
|
||||
}
|
||||
|
||||
UpdateTheme(window);
|
||||
|
||||
return OnCreate();
|
||||
}
|
||||
|
||||
bool Win32Window::Show() {
|
||||
return ShowWindow(window_handle_, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
// static
|
||||
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
|
||||
UINT const message,
|
||||
|
|
@ -188,6 +212,10 @@ Win32Window::MessageHandler(HWND hwnd,
|
|||
SetFocus(child_content_);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_DWMCOLORIZATIONCOLORCHANGED:
|
||||
UpdateTheme(hwnd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProc(window_handle_, message, wparam, lparam);
|
||||
|
|
@ -243,3 +271,18 @@ bool Win32Window::OnCreate() {
|
|||
void Win32Window::OnDestroy() {
|
||||
// No-op; provided for subclasses.
|
||||
}
|
||||
|
||||
void Win32Window::UpdateTheme(HWND const window) {
|
||||
DWORD light_mode;
|
||||
DWORD light_mode_size = sizeof(light_mode);
|
||||
LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
|
||||
kGetPreferredBrightnessRegValue,
|
||||
RRF_RT_REG_DWORD, nullptr, &light_mode,
|
||||
&light_mode_size);
|
||||
|
||||
if (result == ERROR_SUCCESS) {
|
||||
BOOL enable_dark_mode = light_mode == 0;
|
||||
DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||
&enable_dark_mode, sizeof(enable_dark_mode));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,15 +28,16 @@ class Win32Window {
|
|||
Win32Window();
|
||||
virtual ~Win32Window();
|
||||
|
||||
// Creates and shows a win32 window with |title| and position and size using
|
||||
// Creates a win32 window with |title| that is positioned and sized using
|
||||
// |origin| and |size|. New windows are created on the default monitor. Window
|
||||
// sizes are specified to the OS in physical pixels, hence to ensure a
|
||||
// consistent size to will treat the width height passed in to this function
|
||||
// as logical pixels and scale to appropriate for the default monitor. Returns
|
||||
// true if the window was created successfully.
|
||||
bool CreateAndShow(const std::wstring& title,
|
||||
const Point& origin,
|
||||
const Size& size);
|
||||
// consistent size this function will scale the inputted width and height as
|
||||
// as appropriate for the default monitor. The window is invisible until
|
||||
// |Show| is called. Returns true if the window was created successfully.
|
||||
bool Create(const std::wstring& title, const Point& origin, const Size& size);
|
||||
|
||||
// Show the current window. Returns true if the window was successfully shown.
|
||||
bool Show();
|
||||
|
||||
// Release OS resources associated with window.
|
||||
void Destroy();
|
||||
|
|
@ -86,6 +87,9 @@ class Win32Window {
|
|||
// Retrieves a class instance pointer for |window|
|
||||
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
|
||||
|
||||
// Update the window frame's theme to match the system theme.
|
||||
static void UpdateTheme(HWND const window);
|
||||
|
||||
bool quit_on_close_ = false;
|
||||
|
||||
// window handle for top level window.
|
||||
|
|
|
|||
Loading…
Reference in New Issue