From 33c5aae23e5afebb057fbfb26e69a119cca0fe9e Mon Sep 17 00:00:00 2001 From: Vishesh Handa Date: Thu, 6 Aug 2020 12:16:03 +0200 Subject: [PATCH] MarkdownToolBar: First attempt at the toolbar This can be enabled via 'Settings -> Experimental'. It's very rough right now and a lot still needs to be done. Related to #157 --- assets/langs/en.yaml | 1 + lib/editors/markdown_editor.dart | 139 +++++++++++++++++++++++++ lib/screens/settings_experimental.dart | 9 ++ lib/settings.dart | 6 ++ 4 files changed, 155 insertions(+) diff --git a/assets/langs/en.yaml b/assets/langs/en.yaml index 833d535f..d32af315 100644 --- a/assets/langs/en.yaml +++ b/assets/langs/en.yaml @@ -62,6 +62,7 @@ settings: subtitle: Try out features in Development backlinks: Show Backlinks in Markdown Preview fs: Show File System + markdownToolbar: Show Markdown Toolbar in Editor editors: title: Editor Settings subtitle: Configure how different editors work diff --git a/lib/editors/markdown_editor.dart b/lib/editors/markdown_editor.dart index 63fd80d8..21edcdd9 100644 --- a/lib/editors/markdown_editor.dart +++ b/lib/editors/markdown_editor.dart @@ -129,6 +129,23 @@ class MarkdownEditorState extends State Widget body = editingMode ? editor : NoteViewer(note: note); + if (Settings.instance.experimentalMarkdownToolbar && editingMode) { + body = Container( + height: 600, + child: Column( + children: [ + Expanded(child: editor), + MarkdownToolBar( + onHeader1: () => _modifyCurrentLine('# '), + onItallics: () => _modifyCurrentWord('*'), + onBold: () => _modifyCurrentWord('**'), + ), + ], + mainAxisSize: MainAxisSize.min, + ), + ); + } + var extraButton = IconButton( icon: editingMode ? const Icon(Icons.remove_red_eye) @@ -226,4 +243,126 @@ class MarkdownEditorState extends State @override bool get noteModified => _noteModified; + + void _modifyCurrentLine(String char) { + var selection = _textController.value.selection; + var text = _textController.value.text; + + print('Base offset: ${selection.baseOffset}'); + print('Extent offset: ${selection.extentOffset}'); + var cursorPos = selection.baseOffset; + if (cursorPos == -1) { + cursorPos = 0; + } + print('CursorPos: $cursorPos'); + + var lineStartPos = + text.lastIndexOf('\n', cursorPos == 0 ? 0 : cursorPos - 1); + if (lineStartPos == -1) { + lineStartPos = 0; + } + + var lineEndPos = text.indexOf('\n', cursorPos); + if (lineEndPos == -1) { + lineEndPos = text.length; + } + + print('Line Start: $lineStartPos'); + print('Line End: $lineEndPos'); + print('Line: ${text.substring(lineStartPos, lineEndPos)}'); + + // Check if already present + if (text.startsWith(char, lineStartPos)) { + print('Removing `$char`'); + _textController.text = text.replaceFirst(char, '', lineStartPos); + _textController.selection = + TextSelection.collapsed(offset: cursorPos - char.length); + return; + } + + print('Adding `$char`'); + _textController.text = text.replaceRange(lineStartPos, lineStartPos, char); + _textController.selection = + TextSelection.collapsed(offset: cursorPos + char.length); + } + + void _modifyCurrentWord(String char) { + var selection = _textController.value.selection; + var text = _textController.value.text; + + print('Base offset: ${selection.baseOffset}'); + print('Extent offset: ${selection.extentOffset}'); + var cursorPos = selection.baseOffset; + if (cursorPos == -1) { + cursorPos = 0; + } + print('CursorPos: $cursorPos'); + + var wordStartPos = + text.lastIndexOf(' ', cursorPos == 0 ? 0 : cursorPos - 1); + if (wordStartPos == -1) { + wordStartPos = 0; + } + + var wordEndPos = text.indexOf(' ', cursorPos); + if (wordEndPos == -1) { + wordEndPos = text.length; + } + + print('Word Start: $wordStartPos'); + print('Word End: $wordEndPos'); + print('Word: ${text.substring(wordStartPos, wordEndPos)}'); + + // Check if already present + if (text.startsWith(char, wordStartPos)) { + print('Removing `$char`'); + _textController.text = text.replaceFirst(char, '', wordStartPos); + _textController.selection = + TextSelection.collapsed(offset: cursorPos - (char.length * 2)); + return; + } + + print('Adding `$char`'); + _textController.text = text.replaceRange(wordStartPos, wordStartPos, char); + wordEndPos += char.length; + + _textController.text = + text.replaceRange(wordEndPos - 1, wordEndPos - 1, char); + _textController.selection = + TextSelection.collapsed(offset: cursorPos + (char.length * 2)); + + print('$char'); + } +} + +class MarkdownToolBar extends StatelessWidget { + final Function onHeader1; + final Function onItallics; + final Function onBold; + + MarkdownToolBar({ + @required this.onHeader1, + @required this.onItallics, + @required this.onBold, + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + IconButton( + icon: const Text('H1'), + onPressed: onHeader1, + ), + IconButton( + icon: const Text('I'), + onPressed: onItallics, + ), + IconButton( + icon: const Text('B'), + onPressed: onBold, + ), + ], + ); + } } diff --git a/lib/screens/settings_experimental.dart b/lib/screens/settings_experimental.dart index 1197b273..3d9fc557 100644 --- a/lib/screens/settings_experimental.dart +++ b/lib/screens/settings_experimental.dart @@ -47,6 +47,15 @@ class _ExperimentalSettingsScreenState setState(() {}); }, ), + SwitchListTile( + title: Text(tr('settings.experimental.markdownToolbar')), + value: settings.experimentalFs, + onChanged: (bool newVal) { + settings.experimentalFs = newVal; + settings.save(); + setState(() {}); + }, + ), ], padding: const EdgeInsets.fromLTRB(0.0, 16.0, 0.0, 0.0), ), diff --git a/lib/settings.dart b/lib/settings.dart index 3a07b66b..3ca45a4e 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -54,6 +54,7 @@ class Settings { bool experimentalBacklinks = true; bool experimentalFs = false; + bool experimentalMarkdownToolbar = false; bool zenMode = false; bool saveTitleInH1 = true; @@ -123,6 +124,8 @@ class Settings { experimentalBacklinks = pref.getBool("experimentalBacklinks") ?? experimentalBacklinks; experimentalFs = pref.getBool("experimentalFs") ?? experimentalFs; + experimentalMarkdownToolbar = pref.getBool("experimentalMarkdownToolbar") ?? + experimentalMarkdownToolbar; zenMode = pref.getBool("zenMode") ?? zenMode; saveTitleInH1 = pref.getBool("saveTitleInH1") ?? saveTitleInH1; @@ -194,6 +197,8 @@ class Settings { _setBool(pref, "experimentalBacklinks", experimentalBacklinks, defaultSet.experimentalBacklinks); _setBool(pref, "experimentalFs", experimentalFs, defaultSet.experimentalFs); + _setBool(pref, "experimentalMarkdownToolbar", experimentalMarkdownToolbar, + defaultSet.experimentalMarkdownToolbar); _setBool(pref, "zenMode", zenMode, defaultSet.zenMode); _setBool(pref, "saveTitleInH1", saveTitleInH1, defaultSet.saveTitleInH1); @@ -257,6 +262,7 @@ class Settings { 'debugLogLevel': debugLogLevel, 'experimentalBacklinks': experimentalBacklinks.toString(), 'experimentalFs': experimentalFs.toString(), + 'experimentalMarkdownToolbar': experimentalMarkdownToolbar.toString(), 'zenMode': zenMode.toString(), 'saveTitleInH1': saveTitleInH1.toString(), };