├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README_CN.md ├── analysis_options.yaml ├── arts ├── art01.gif └── art02.png ├── example ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ ├── code.dart │ ├── code.json │ └── large.txt ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ └── RunnerTests │ │ └── RunnerTests.swift ├── lib │ ├── editor_autocomplete.dart │ ├── editor_basic_field.dart │ ├── editor_json.dart │ ├── editor_large_text.dart │ ├── find.dart │ ├── main.dart │ └── menu.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ ├── CMakeLists.txt │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugin_registrant.h │ │ └── generated_plugins.cmake │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_512.png │ │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ │ └── MainMenu.xib │ │ ├── Configs │ │ │ ├── AppInfo.xcconfig │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements │ └── RunnerTests │ │ └── RunnerTests.swift ├── pubspec.lock ├── pubspec.yaml ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ ├── Icon-512.png │ │ ├── Icon-maskable-192.png │ │ └── Icon-maskable-512.png │ ├── index.html │ └── manifest.json └── windows │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake │ └── runner │ ├── CMakeLists.txt │ ├── Runner.rc │ ├── flutter_window.cpp │ ├── flutter_window.h │ ├── main.cpp │ ├── resource.h │ ├── resources │ └── app_icon.ico │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── lib ├── re_editor.dart └── src │ ├── _code_autocomplete.dart │ ├── _code_editable.dart │ ├── _code_extensions.dart │ ├── _code_field.dart │ ├── _code_find.dart │ ├── _code_floating_cursor.dart │ ├── _code_formatter.dart │ ├── _code_highlight.dart │ ├── _code_indicator.dart │ ├── _code_input.dart │ ├── _code_line.dart │ ├── _code_lines.dart │ ├── _code_paragraph.dart │ ├── _code_scroll.dart │ ├── _code_selection.dart │ ├── _code_shortcuts.dart │ ├── _code_span.dart │ ├── _consts.dart │ ├── _isolate.dart │ ├── code_autocomplete.dart │ ├── code_chunk.dart │ ├── code_editor.dart │ ├── code_find.dart │ ├── code_formatter.dart │ ├── code_indicator.dart │ ├── code_line.dart │ ├── code_lines.dart │ ├── code_paragraph.dart │ ├── code_scroll.dart │ ├── code_shortcuts.dart │ ├── code_span.dart │ ├── code_theme.dart │ ├── code_toolbar.dart │ ├── debug │ └── _trace.dart │ └── re_editor.dart ├── pubspec.yaml └── test ├── code_chunk_default_analyzer_test.dart ├── code_chunk_test.dart ├── code_line_editing_controller_test.dart ├── code_line_editing_value_test.dart ├── code_line_position_test.dart ├── code_line_range_test.dart ├── code_line_selection_test.dart ├── code_line_test.dart ├── code_line_utils_test.dart ├── code_lines_test.dart ├── code_search_controller_test.dart ├── code_search_option_test.dart ├── code_search_result_test.dart ├── code_search_value_test.dart └── data ├── json_flatted.json └── json_pretty.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: MegatronKing 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Device:** 27 | - OS: [e.g. iOS] 28 | - Version [e.g. 22] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: MegatronKing 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 25 | /pubspec.lock 26 | **/doc/api/ 27 | .dart_tool/ 28 | .packages 29 | build/ 30 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 7e9793dee1b85a243edd0e06cb1658e98b077561 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "re-editor", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "re-editor (profile mode)", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile" 17 | }, 18 | { 19 | "name": "re-editor (release mode)", 20 | "request": "launch", 21 | "type": "dart", 22 | "flutterMode": "release" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * First Release Version. 4 | 5 | ## 0.0.2 6 | 7 | * Update pubspec.yaml and README. 8 | 9 | ## 0.0.3 10 | 11 | * Update publisher 12 | 13 | ## 0.0.4 14 | 15 | * Update re-highlight version. 16 | * Fix unmounted issue. 17 | 18 | ## 0.1.0 19 | * [IMP] Break changes: refactor code autocomplete API. 20 | * [IMP] Break changes: add more params for `CodeLineSpanBuilder`. 21 | * Make `CodeEditorTapRegion` to public. 22 | * Add `MouseTrackerAnnotationTextSpan` for hovering support. 23 | * Add `CodeLineEditingControllerDelegate` for delegating the controller. 24 | * Add an API for editor force repaint. 25 | 26 | ## 0.1.1 27 | * Clamp mode selection handle positions for mobile. 28 | * Add an auto scrolling list for autocomplete example. 29 | * Fix mobile selection handle invisible issue. 30 | * Delete shift key selection logic for mobile. 31 | * Fix newline action not works with some android input methods. 32 | * Fix a bug that the mobile input keyboard does not popup when tapping the editor. 33 | 34 | ## 0.2.0 35 | * Web support. 36 | * Mobile toolbar widget will be built by user rather than the editor. 37 | * Autocomplete will only update after user input. 38 | * Fix desktop focus issue. 39 | 40 | ## 0.3.0 41 | * [IMP] Break changes: remove API `CodeLineEditingController.fromFile`. 42 | * Feature: singleline chunks comment. 43 | * Feature: multiline comment formatter 44 | * Feature: moveCursorToWordBoundary and extendSelectionToWordBoundary(forward and backward). 45 | 46 | ## 0.3.1 47 | * Fix space key not works in PageView. 48 | * Notify delegate listeners when controller was changed. 49 | * Remove editable shortcut actions when read only. 50 | 51 | ## 0.4.0 52 | * Feature: add shortcuts for word and line direction deleting. 53 | * Feature: add an option to disable autocomplete closed symbols. 54 | * Feature: add borderRadius and clipBehavior properties to editor settings. 55 | * Feature:allow clearing of CodeLineEditingController's undo and redo stack. 56 | * Opt autocomplete quoted symbol logic. 57 | * Fix `IsolateCallback` might be invoked after the isolate was closed. 58 | * Fix controller delegate memory leak issue. 59 | * Fix issue `xxx is used after being disposed`. 60 | * Fix the bug that the underlying text will be selected when trying to click-drag the scroll bar. 61 | 62 | ## 0.5.0 63 | * Fix code lint warnings in the example project. 64 | * Fix double newline issue on iOS. 65 | * Allow the gesture pointer overflow when dragging to select text. 66 | * Give an option to tell editor the size of the custom scrollbar. 67 | 68 | ## 0.6.0 69 | * Check tap down pointer whether is in the valid region. 70 | * Break changes: Change autocomplete behavior to replace user input with complete match to support more pattern match method. 71 | 72 | ## 0.7.0 73 | * Add callback parameter to CodeLineNumberRenderObject for customizable line number behavior. 74 | * Implement floating cursor feature for iOS. 75 | * Long press will select a word on mobile platform. 76 | * Disable ESC shortcuts when nothing the editor can do. 77 | * Added option for maximum rendering length of single line text. 78 | * Fix makePositionCenterIfInvisible was called infinitely. 79 | * Clamp code line substring. 80 | * Fixed a bug: type 'Null' is not a subtype of type '_CodeFieldRender'. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Reqable 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Re-Editor 2 | 3 | [![latest version](https://img.shields.io/pub/v/re_editor.svg?color=blue)](https://pub.dev/packages/re_editor) 4 | 5 | [中文版本](./README_CN.md) 6 | 7 | `Re-Editor` is a powerful lightweight text and code editor widget and a module in the [Reqable](https://reqable.com) project. It can be used as a simple text area or to develop a code editor with complex functions. Unlike Flutter's default `TextField`, `Re-Editor` is specifically tailored for the display and input of multi-line text and offers the following features: 8 | 9 | - Two-way horizontal and vertical scrolling. 10 | - Text syntax highlighting. 11 | - Content collapsing and expanding. 12 | - Input hints and auto-completion. 13 | - Search and replace. 14 | - Custom context menu builder. 15 | - Shortcut keys. 16 | - Large text display and editing. 17 | - Line numbers and focus line builder. 18 | - Smart input. 19 | 20 | `Re-Editor` is not a secondary encapsulation based on `TextField`, but independently implements the layout, drawing, event processing, etc. It is specifically optimized for large texts, providing extremely high performance and fixed some issues of `TextField`. 21 | 22 | `Re-Editor` offers a high degree of freedom. For example, developers can control whether to enable horizontal scrolling (word wrap), enable read-only mode, display line numbers, display content folding, define custom shortcut keys, and specify text syntax highlighting. 23 | 24 | You can run the `example` project to experience it. 25 | 26 | ![](arts/art01.gif) 27 | 28 | ## Getting Started 29 | 30 | Add the followings in `pubspec.yaml`. 31 | 32 | ```yaml 33 | dependencies: 34 | re_editor: ^0.7.0 35 | ``` 36 | 37 | Like `TextField`, `Re-Editor` uses `CodeLineEditingController` as the controller. The following sample code creates the simplest multi-line input area, which is not much different from `TextField`. 38 | 39 | ```dart 40 | Widget build(BuildContext context) { 41 | return CodeEditor( 42 | controller: CodeLineEditingController.fromText('Hello Reqable'), 43 | ); 44 | } 45 | ``` 46 | 47 | ### Text Syntax Highlighting 48 | 49 | The text highlighting of `Re-Editor` is based on [Re-Highlight](https://github.com/reqable/re-highlight) and supports nearly a hundred languages ​​and theme styles. Developers can freely choose and configure the code Highlight. The following code specifies the `JSON` syntax highlighting rules and applies the `Atom One Light` code coloring. 50 | 51 | ```dart 52 | CodeEditor( 53 | style: CodeEditorStyle( 54 | codeTheme: CodeHighlightTheme( 55 | languages: { 56 | 'json': CodeHighlightThemeMode( 57 | mode: langJson 58 | ) 59 | }, 60 | theme: atomOneLightTheme 61 | ), 62 | ), 63 | ); 64 | ``` 65 | 66 | ### Line Numbers and Fold/Unfold Markers 67 | 68 | `Re-Editor` supports configuring whether to display code line numbers and code folding marks, and developers can also implement display styles and layouts by themselves. The example code below shows the default style, built with `indicatorBuilder`. 69 | 70 | ```dart 71 | CodeEditor( 72 | indicatorBuilder: (context, editingController, chunkController, notifier) { 73 | return Row( 74 | children: [ 75 | DefaultCodeLineNumber( 76 | controller: editingController, 77 | notifier: notifier, 78 | ), 79 | DefaultCodeChunkIndicator( 80 | width: 20, 81 | controller: chunkController, 82 | notifier: notifier 83 | ) 84 | ], 85 | ); 86 | }, 87 | ); 88 | ``` 89 | 90 | ### Code Folding and Unfolding Detection 91 | 92 | By default, `Re-Editor` will automatically detect the folding areas of `{}` and `[]`. Developers can control whether to detect or write their own detection rules. `DefaultCodeChunkAnalyzer` is the default detector. If you wish to disable detection, you can use `NonCodeChunkAnalyzer`. 93 | 94 | ```dart 95 | CodeEditor( 96 | chunkAnalyzer: DefaultCodeChunkAnalyzer(), 97 | ); 98 | ``` 99 | 100 | If you want to customize it, just implement the `CodeChunkAnalyzer` interface. 101 | 102 | ```dart 103 | abstract class CodeChunkAnalyzer { 104 | 105 | List run(CodeLines codeLines); 106 | 107 | } 108 | ``` 109 | 110 | ### Scroll Control 111 | 112 | `Re-Editor` supports two-way scrolling, so two `ScrollController` are used, and developers can use `CodeScrollController` to construct. 113 | 114 | ```dart 115 | CodeEditor( 116 | scrollController: CodeScrollController( 117 | verticalScroller: ScrollController(), 118 | horizontalScroller: ScrollController(), 119 | ) 120 | ); 121 | ``` 122 | 123 | ### Find and Replace 124 | 125 | `Re-Editor` implements search and replace control logic, but does not provide a default UI. Developers need to write the UI of the search panel according to the actual situation of their own projects, and use the `findBuilder` attribute to set up their own search and replace UI. 126 | 127 | ```dart 128 | CodeEditor( 129 | findBuilder: (context, controller, readOnly) => CodeFindPanelView(controller: controller, readOnly: readOnly), 130 | ); 131 | ``` 132 | 133 | The `CodeFindPanelView` in the above example is implemented by the developer himself. For the detailed implementation process, please refer to the code in `example`. 134 | 135 | ### Context Menu 136 | 137 | `Re-Editor` implements the control logic of the desktop context menu and the mobile long-press selection menu, but does not provide a default UI. Developers need to implement the `SelectionToolbarController` interface and setup it through `toolbarController`. 138 | 139 | ```dart 140 | CodeEditor( 141 | toolbarController: _MyToolbarController(), 142 | ); 143 | ``` 144 | 145 | ### Shortcuts 146 | 147 | `Re-Editor` has the built-in default shortcut hotkeys, and developers can also use `shortcutsActivatorsBuilder` to set custom shortcut hotkeys. Of course, the shortcut keys only work on the desktop. 148 | 149 | The shortcut keys supported by `Re-Editor` are as follows: 150 | - Select all (Control/Command + A) 151 | - Cut selected/current line (Control/Command + V) 152 | - Copy selected/current line (Control/Command + C) 153 | - Paste (Control/Command + V) 154 | - Undo (Control/Command + Z) 155 | - Redo (Shift + Control/Command + Z) 156 | - Select the current line (Control/Command + L) 157 | - Delete current line (Control/Command + D) 158 | - Move current line (Alt + ↑/↓) 159 | - Continuous selection (Shift + ↑/↓/←/→) 160 | - Move cursor (↑/↓/←/→) 161 | - Move cursor between word boundaries (Alt + ←/→) 162 | - Move to top/bottom of page (Control/Command + ↑/↓) 163 | - Indent (Tab) 164 | - Unindent (Shift + Tab) 165 | - Comment/uncomment a single line (Control/Command + /) 166 | - Comment/uncomment multiple lines (Shift + Control/Command + /) 167 | - Character transpose (Control/Command + T) 168 | - Search (Control/Command + F) 169 | - Replace (Alt + Control/Command + F) 170 | - Save (Control/Command + S) 171 | 172 | ### Code Hints and Auto-Completion 173 | 174 | `Re-Editor` supports using the `CodeAutocomplete` widget to implement code input prompts and automatic completion. `Re-Editor` implements basic control logic, but the code prompt content, auto-completion rules and display UI need to be defined by the developer. 175 | 176 | ```dart 177 | CodeAutocomplete( 178 | viewBuilder: (context, notifier, onSelected) { 179 | // build the code prompts view 180 | }, 181 | promptsBuilder: DefaultCodeAutocompletePromptsBuilder( 182 | language: langDart, 183 | ), 184 | child: CodeEditor() 185 | ); 186 | ``` 187 | 188 | Note that `Re-Editor` is only a lightweight editor and does not have the IDE dynamic syntax analysis, so the code prompts and completion have many limitations. You can refer to the code in `example` to implement a simple code prompt and completion. 189 | 190 | ## Used By 191 | 192 | `Re-Editor` has been extensively practiced in the Reqable project. You are welcome to download [Reqable](https://reqable.com/download) to experience it. 193 | 194 | ![](arts/art02.png) 195 | 196 | ## License 197 | 198 | MIT License 199 | 200 | ## Sponsor 201 | 202 | If you would like to sponsor this project, you can support us by purchasing a [Reqable](https://reqable.com/pricing) license. 203 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # Re-Editor 2 | 3 | [![latest version](https://img.shields.io/pub/v/re_editor.svg?color=blue)](https://pub.dev/packages/re_editor) 4 | 5 | `Re-Editor`是一个强大的轻量级文本和代码编辑器组件,是[Reqable](https://reqable.com)项目中的一个模块。`Re-Editor`既可以作为一个简单文本输入组件,也可以用来开发一个功能复杂的代码编辑器。和Flutter官方默认的`TextField`组件不一样的是,`Re-Editor`是专为多行文本的显示和输入量身定制,具备下面这些特性: 6 | 7 | - 横向和纵向双向滚动。 8 | - 文本语法高亮。 9 | - 内容折叠和展开。 10 | - 输入提示和自动补全。 11 | - 搜索替换功能。 12 | - 自定义上下文菜单。 13 | - 快捷键支持。 14 | - 大文本显示和编辑。 15 | - 显示行号和焦点行。 16 | - 智能输入。 17 | 18 | `Re-Editor`并非基于`TextField`进行二次封装,而是自行实现了各项元素的布局、绘制、事件处理等,针对大文本进行了特定优化,因此具备极高的性能,同时解决了`TextField`的各项痛点。 19 | 20 | `Re-Editor`提供了非常大的自由度,例如开发者可以控制是否横行滚动(Word Wrap),是否只读,是否显示行号,是否显示分析内容折叠,自定义快捷键,指定文本高亮语法等等。 21 | 22 | 您可以运行`example`项目进行体验各项功能。 23 | 24 | ![](arts/art01.gif) 25 | 26 | ## 开始使用 27 | 28 | 添加依赖到 `pubspec.yaml`. 29 | 30 | ```yaml 31 | dependencies: 32 | re_editor: ^0.7.0 33 | ``` 34 | 35 | 和`TextField`一样,`Re-Editor`使用`CodeLineEditingController`作为控制器,下面的示例代码创建了一个最简单的编辑器组件,在显示样式上和`TextField`并没有什么太大的区别。 36 | ```dart 37 | Widget build(BuildContext context) { 38 | return CodeEditor( 39 | controller: CodeLineEditingController.fromText('Hello Reqable'), 40 | ); 41 | } 42 | ``` 43 | 44 | ### 文本高亮 45 | 46 | `Re-Editor`的文本高亮是基于[Re-Highlight](https://github.com/reqable/re-highlight)实现,支持近百种语言和主题样式,开发者可以自由选择并配置代码高亮。下面的代码就指定了`JSON`语法高亮规则以及应用了`Atom One Light`代码配色。 47 | ```dart 48 | CodeEditor( 49 | style: CodeEditorStyle( 50 | codeTheme: CodeHighlightTheme( 51 | languages: { 52 | 'json': CodeHighlightThemeMode( 53 | mode: langJson 54 | ) 55 | }, 56 | theme: atomOneLightTheme 57 | ), 58 | ), 59 | ); 60 | ``` 61 | 62 | ### 行号以及折叠/展开标记 63 | 64 | `Re-Editor`支持配置是否显示代码行号和代码折叠标记,也可以由开发者自行实现显示样式和排版。下面的示例代码显示了默认的样式,通过`indicatorBuilder`来构建。 65 | ```dart 66 | CodeEditor( 67 | indicatorBuilder: (context, editingController, chunkController, notifier) { 68 | return Row( 69 | children: [ 70 | DefaultCodeLineNumber( 71 | controller: editingController, 72 | notifier: notifier, 73 | ), 74 | DefaultCodeChunkIndicator( 75 | width: 20, 76 | controller: chunkController, 77 | notifier: notifier 78 | ) 79 | ], 80 | ); 81 | }, 82 | ); 83 | ``` 84 | 85 | ### 代码折叠展开检测 86 | 87 | 默认情况下,`Re-Editor`会自动检测`{}`和`[]`的折叠区域,开发者可以控制是否检测,也可以自行编写检测规则。`DefaultCodeChunkAnalyzer`是默认的检测器,如果希望禁用检测,可以使用`NonCodeChunkAnalyzer`。 88 | ```dart 89 | CodeEditor( 90 | chunkAnalyzer: DefaultCodeChunkAnalyzer(), 91 | ); 92 | 93 | ``` 94 | 95 | 如果希望自定义,实现`CodeChunkAnalyzer`接口即可。 96 | 97 | ```dart 98 | abstract class CodeChunkAnalyzer { 99 | 100 | List run(CodeLines codeLines); 101 | 102 | } 103 | ``` 104 | 105 | ### 滚动控制 106 | 107 | `Re-Editor`支持双向滚动,因此使用了两个`ScrollController`,开发者可以用`CodeScrollController`进行构造。 108 | ```dart 109 | CodeEditor( 110 | scrollController: CodeScrollController( 111 | verticalScroller: ScrollController(), 112 | horizontalScroller: ScrollController(), 113 | ) 114 | ); 115 | ``` 116 | 117 | ### 搜索和替换 118 | 119 | `Re-Editor`实现了持搜索和替换逻辑,但是并没有提供默认的样式。开发者需要根据自己项目的实际情况编写搜索面板的UI,使用`findBuilder`属性来设置自己实现的搜索和替换UI。 120 | 121 | ```dart 122 | CodeEditor( 123 | findBuilder: (context, controller, readOnly) => CodeFindPanelView(controller: controller, readOnly: readOnly), 124 | ); 125 | ``` 126 | 127 | 上面示例中的`CodeFindPanelView`由开发者自己实现,详细实现过程可以参考`example`中的代码。 128 | 129 | ### 上下文菜单 130 | 131 | `Re-Editor`实现了桌面端右键菜单和移动端长按选中菜单的控制逻辑,但是并没有提供默认的样式。开发者需要实现`SelectionToolbarController`接口,并通过`toolbarController`进行配置。 132 | 133 | ```dart 134 | CodeEditor( 135 | toolbarController: _MyToolbarController(), 136 | ); 137 | ``` 138 | 139 | ### 快捷键 140 | 141 | `Re-Editor`内置了默认的快捷键功能,开发者也可以使用`shortcutsActivatorsBuilder`来设置自定义的快捷热键。当然,快捷键功能仅在桌面端有效。 142 | 143 | `Re-Editor`支持的快捷键功能如下: 144 | - 全选(Control/Command + A) 145 | - 剪切选中/当前行(Control/Command + V) 146 | - 复制选中/当前行(Control/Command + C) 147 | - 粘贴(Control/Command + V) 148 | - 撤销(Control/Command + Z) 149 | - 重做(Shift + Control/Command + Z) 150 | - 选中当前行(Control/Command + L) 151 | - 删除当前行(Control/Command + D) 152 | - 移动当前行(Alt + ↑/↓) 153 | - 连续选择(Shift + ↑/↓/←/→) 154 | - 移动光标(↑/↓/←/→) 155 | - 移动光标(单词边界) (Alt + ←/→) 156 | - 移动到页首/页尾(Control/Command + ↑/↓) 157 | - 缩进(Tab) 158 | - 取消缩进(Shift + Tab) 159 | - 注释/取消单行注释(Control/Command + /) 160 | - 注释/取消多行注释(Shift + Control/Command + /) 161 | - 字符转换(Control/Command + T) 162 | - 搜索(Control/Command + F) 163 | - 替换(Alt + Control/Command + F) 164 | - 保存(Control/Command + S) 165 | 166 | ### 代码提示和补全 167 | 168 | `Re-Editor`支持使用`CodeAutocomplete`组件来实现代码输入提示和自动补全。`Re-Editor`实现了基本的控制逻辑,但是代码提示内容、自动补全规则和显示样式需要由开发者来自行定义。 169 | 170 | ```dart 171 | CodeAutocomplete( 172 | viewBuilder: (context, notifier, onSelected) { 173 | // 创建代码提示View 174 | }, 175 | promptsBuilder: DefaultCodeAutocompletePromptsBuilder( 176 | language: langDart, 177 | ), 178 | child: CodeEditor() 179 | ); 180 | ``` 181 | 182 | 注意,`Re-Editor`只是一个轻量级的编辑器,不具备IDE动态语法分析功能,因此代码提示和补全功能存在较多的局限性。您可以参考`example`中的代码实现一个简单的代码提示和补全功能。 183 | 184 | ## 实现范例 185 | 186 | `Re-Editor`在Reqable项目中有着深度实践,欢迎下载 [Reqable](https://reqable.com/download) 进行体验。 187 | 188 | ![](arts/art02.png) 189 | 190 | ## 许可证 191 | 192 | MIT License 193 | 194 | ## 赞助 195 | 196 | 如果您希望赞助本项目,可以通过购买[Reqable](https://reqable.com/pricing)的许可证来赞助我们。 -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /arts/art01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/arts/art01.gif -------------------------------------------------------------------------------- /arts/art02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/arts/art02.png -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.example.example" 27 | compileSdkVersion flutter.compileSdkVersion 28 | ndkVersion flutter.ndkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example.example" 46 | // You can update the following values to match your application needs. 47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 48 | minSdkVersion flutter.minSdkVersion 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies {} 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | mavenCentral() 17 | } 18 | } 19 | 20 | rootProject.buildDir = '../build' 21 | subprojects { 22 | project.buildDir = "${rootProject.buildDir}/${project.name}" 23 | } 24 | subprojects { 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | tasks.register("clean", Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false 21 | } 22 | } 23 | 24 | plugins { 25 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 26 | id "com.android.application" version "7.3.0" apply false 27 | } 28 | 29 | include ":app" 30 | -------------------------------------------------------------------------------- /example/assets/code.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | class ReEditor { 4 | 5 | final String foo; 6 | final String bar; 7 | 8 | ReEditor(this.foo, this.bar); 9 | 10 | void hello(String name) { 11 | if (kDebugMode) { 12 | print('hello $name'); 13 | } 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /example/assets/code.json: -------------------------------------------------------------------------------- 1 | { 2 | "configVersion": 2, 3 | "packages": [ 4 | { 5 | "name": "async", 6 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/async-2.11.0", 7 | "packageUri": "lib/", 8 | "languageVersion": "2.18" 9 | }, 10 | { 11 | "name": "boolean_selector", 12 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/boolean_selector-2.1.1", 13 | "packageUri": "lib/", 14 | "languageVersion": "2.17" 15 | }, 16 | { 17 | "name": "characters", 18 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/characters-1.3.0", 19 | "packageUri": "lib/", 20 | "languageVersion": "2.12" 21 | }, 22 | { 23 | "name": "clock", 24 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/clock-1.1.1", 25 | "packageUri": "lib/", 26 | "languageVersion": "2.12" 27 | }, 28 | { 29 | "name": "collection", 30 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/collection-1.18.0", 31 | "packageUri": "lib/", 32 | "languageVersion": "2.18" 33 | }, 34 | { 35 | "name": "fake_async", 36 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/fake_async-1.3.1", 37 | "packageUri": "lib/", 38 | "languageVersion": "2.12" 39 | }, 40 | { 41 | "name": "flutter", 42 | "rootUri": "file:///Users/megatronking/Workspace/Flutter/packages/flutter", 43 | "packageUri": "lib/", 44 | "languageVersion": "3.2" 45 | }, 46 | { 47 | "name": "flutter_lints", 48 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/flutter_lints-2.0.3", 49 | "packageUri": "lib/", 50 | "languageVersion": "2.19" 51 | }, 52 | { 53 | "name": "flutter_test", 54 | "rootUri": "file:///Users/megatronking/Workspace/Flutter/packages/flutter_test", 55 | "packageUri": "lib/", 56 | "languageVersion": "3.2" 57 | }, 58 | { 59 | "name": "lints", 60 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/lints-2.1.1", 61 | "packageUri": "lib/", 62 | "languageVersion": "3.0" 63 | }, 64 | { 65 | "name": "matcher", 66 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/matcher-0.12.16", 67 | "packageUri": "lib/", 68 | "languageVersion": "2.18" 69 | }, 70 | { 71 | "name": "material_color_utilities", 72 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/material_color_utilities-0.5.0", 73 | "packageUri": "lib/", 74 | "languageVersion": "2.17" 75 | }, 76 | { 77 | "name": "meta", 78 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/meta-1.10.0", 79 | "packageUri": "lib/", 80 | "languageVersion": "2.12" 81 | }, 82 | { 83 | "name": "path", 84 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/path-1.8.3", 85 | "packageUri": "lib/", 86 | "languageVersion": "2.12" 87 | }, 88 | { 89 | "name": "sky_engine", 90 | "rootUri": "file:///Users/megatronking/Workspace/Flutter/bin/cache/pkg/sky_engine", 91 | "packageUri": "lib/", 92 | "languageVersion": "3.2" 93 | }, 94 | { 95 | "name": "source_span", 96 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/source_span-1.10.0", 97 | "packageUri": "lib/", 98 | "languageVersion": "2.18" 99 | }, 100 | { 101 | "name": "stack_trace", 102 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/stack_trace-1.11.1", 103 | "packageUri": "lib/", 104 | "languageVersion": "2.18" 105 | }, 106 | { 107 | "name": "stream_channel", 108 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/stream_channel-2.1.2", 109 | "packageUri": "lib/", 110 | "languageVersion": "2.19" 111 | }, 112 | { 113 | "name": "string_scanner", 114 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/string_scanner-1.2.0", 115 | "packageUri": "lib/", 116 | "languageVersion": "2.18" 117 | }, 118 | { 119 | "name": "term_glyph", 120 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/term_glyph-1.2.1", 121 | "packageUri": "lib/", 122 | "languageVersion": "2.12" 123 | }, 124 | { 125 | "name": "test_api", 126 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/test_api-0.6.1", 127 | "packageUri": "lib/", 128 | "languageVersion": "3.0" 129 | }, 130 | { 131 | "name": "vector_math", 132 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/vector_math-2.1.4", 133 | "packageUri": "lib/", 134 | "languageVersion": "2.14" 135 | }, 136 | { 137 | "name": "web", 138 | "rootUri": "file:///Users/megatronking/.pub-cache/hosted/pub.dev/web-0.3.0", 139 | "packageUri": "lib/", 140 | "languageVersion": "3.2" 141 | }, 142 | { 143 | "name": "re_highlight", 144 | "rootUri": "../", 145 | "packageUri": "lib/", 146 | "languageVersion": "2.17" 147 | } 148 | ], 149 | "generated": "2024-02-03T08:26:42.912338Z", 150 | "generator": "pub", 151 | "generatorVersion": "3.2.3" 152 | } 153 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/editor_basic_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:re_editor/re_editor.dart'; 3 | 4 | class BasicField extends StatelessWidget { 5 | 6 | const BasicField({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return CodeEditor( 11 | wordWrap: false, 12 | controller: CodeLineEditingController.fromText(('${'Hello Reqable💐👏 ' * 10}\n') * 100), 13 | ); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /example/lib/editor_json.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:re_editor/re_editor.dart'; 4 | import 'package:re_editor_exmaple/find.dart'; 5 | import 'package:re_editor_exmaple/menu.dart'; 6 | import 'package:re_highlight/languages/json.dart'; 7 | import 'package:re_highlight/styles/atom-one-light.dart'; 8 | 9 | class JsonEditor extends StatefulWidget { 10 | 11 | const JsonEditor({super.key}); 12 | 13 | @override 14 | State createState() => _JsonEditorState(); 15 | 16 | } 17 | 18 | class _JsonEditorState extends State { 19 | 20 | final CodeLineEditingController _controller = CodeLineEditingController(); 21 | 22 | @override 23 | void initState() { 24 | rootBundle.loadString('assets/code.json').then((value) { 25 | _controller.text = value; 26 | }); 27 | super.initState(); 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return CodeEditor( 33 | style: CodeEditorStyle( 34 | codeTheme: CodeHighlightTheme( 35 | languages: { 36 | 'json': CodeHighlightThemeMode( 37 | mode: langJson 38 | ) 39 | }, 40 | theme: atomOneLightTheme 41 | ), 42 | ), 43 | controller: _controller, 44 | wordWrap: false, 45 | indicatorBuilder: (context, editingController, chunkController, notifier) { 46 | return Row( 47 | children: [ 48 | DefaultCodeLineNumber( 49 | controller: editingController, 50 | notifier: notifier, 51 | ), 52 | DefaultCodeChunkIndicator( 53 | width: 20, 54 | controller: chunkController, 55 | notifier: notifier 56 | ) 57 | ], 58 | ); 59 | }, 60 | findBuilder: (context, controller, readOnly) => CodeFindPanelView(controller: controller, readOnly: readOnly), 61 | toolbarController: const ContextMenuControllerImpl(), 62 | sperator: Container( 63 | width: 1, 64 | color: Colors.blue 65 | ), 66 | ); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /example/lib/editor_large_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:re_editor/re_editor.dart'; 4 | import 'package:re_editor_exmaple/find.dart'; 5 | import 'package:re_editor_exmaple/menu.dart'; 6 | 7 | class LargeTextEditor extends StatefulWidget { 8 | 9 | const LargeTextEditor({super.key}); 10 | 11 | @override 12 | State createState() => _LargeTextEditorState(); 13 | 14 | } 15 | 16 | class _LargeTextEditorState extends State { 17 | 18 | final CodeLineEditingController _controller = CodeLineEditingController(); 19 | 20 | @override 21 | void initState() { 22 | rootBundle.loadString('assets/large.txt').then((value) { 23 | _controller.text = value; 24 | }); 25 | super.initState(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return CodeEditor( 31 | controller: _controller, 32 | wordWrap: false, 33 | indicatorBuilder: (context, editingController, chunkController, notifier) { 34 | return Row( 35 | children: [ 36 | DefaultCodeLineNumber( 37 | controller: editingController, 38 | notifier: notifier, 39 | ), 40 | DefaultCodeChunkIndicator( 41 | width: 20, 42 | controller: chunkController, 43 | notifier: notifier 44 | ) 45 | ], 46 | ); 47 | }, 48 | findBuilder: (context, controller, readOnly) => CodeFindPanelView(controller: controller, readOnly: readOnly), 49 | toolbarController: const ContextMenuControllerImpl(), 50 | sperator: Container( 51 | width: 1, 52 | color: Colors.blue 53 | ), 54 | ); 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:re_editor_exmaple/editor_autocomplete.dart'; 4 | import 'package:re_editor_exmaple/editor_basic_field.dart'; 5 | import 'package:re_editor_exmaple/editor_json.dart'; 6 | import 'package:re_editor_exmaple/editor_large_text.dart'; 7 | 8 | void main() { 9 | runApp(const MyApp()); 10 | } 11 | 12 | class MyApp extends StatelessWidget { 13 | const MyApp({Key? key}) : super(key: key); 14 | 15 | // This widget is the root of your application. 16 | @override 17 | Widget build(BuildContext context) { 18 | return MaterialApp( 19 | title: 'Re-Editor', 20 | theme: ThemeData( 21 | colorScheme: const ColorScheme.light( 22 | primary: Color.fromARGB(255, 255, 140, 0), 23 | ) 24 | ), 25 | home: const MyHomePage(title: 'Re-Editor Demo Page'), 26 | ); 27 | } 28 | } 29 | 30 | class MyHomePage extends StatefulWidget { 31 | const MyHomePage({Key? key, required this.title}) : super(key: key); 32 | 33 | // This widget is the home page of your application. It is stateful, meaning 34 | // that it has a State object (defined below) that contains fields that affect 35 | // how it looks. 36 | 37 | // This class is the configuration for the state. It holds the values (in this 38 | // case the title) provided by the parent (in this case the App widget) and 39 | // used by the build method of the State. Fields in a Widget subclass are 40 | // always marked "final". 41 | 42 | final String title; 43 | 44 | @override 45 | State createState() => _MyHomePageState(); 46 | } 47 | 48 | class _MyHomePageState extends State { 49 | 50 | static const Map _editors = { 51 | 'Basic Field': BasicField(), 52 | 'Json Editor': JsonEditor(), 53 | 'Auto Complete': AutoCompleteEditor(), 54 | 'Large Text': LargeTextEditor(), 55 | }; 56 | 57 | int _index = 0; 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | final Widget child = _editors.values.elementAt(_index); 62 | return Scaffold( 63 | appBar: AppBar( 64 | title: Text(widget.title), 65 | ), 66 | body: Container( 67 | margin: const EdgeInsets.all(20), 68 | child: Column( 69 | children: [ 70 | SingleChildScrollView( 71 | scrollDirection: Axis.horizontal, 72 | child: Row( 73 | children: _editors.entries.mapIndexed((index, entry) { 74 | return TextButton( 75 | onPressed: () { 76 | setState(() { 77 | _index = index; 78 | }); 79 | }, 80 | child: Text( 81 | entry.key, 82 | style: TextStyle( 83 | color: _index == index ? null : Colors.black 84 | ), 85 | ), 86 | ); 87 | }).toList(), 88 | ), 89 | ), 90 | Expanded( 91 | child: Container( 92 | decoration: BoxDecoration( 93 | border: Border.all( 94 | color: Colors.grey 95 | ) 96 | ), 97 | child: child, 98 | ) 99 | ) 100 | ], 101 | ) 102 | ), 103 | ); 104 | } 105 | } -------------------------------------------------------------------------------- /example/lib/menu.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:re_editor/re_editor.dart'; 3 | 4 | class ContextMenuItemWidget extends PopupMenuItem implements PreferredSizeWidget { 5 | 6 | ContextMenuItemWidget({ 7 | Key? key, 8 | required String text, 9 | required VoidCallback onTap, 10 | }) : super( 11 | key: key, 12 | onTap: onTap, 13 | child: Text(text) 14 | ); 15 | 16 | @override 17 | Size get preferredSize => const Size(150, 25); 18 | 19 | } 20 | 21 | class ContextMenuControllerImpl implements SelectionToolbarController { 22 | 23 | const ContextMenuControllerImpl(); 24 | 25 | @override 26 | void hide(BuildContext context) { 27 | } 28 | 29 | @override 30 | void show({ 31 | required BuildContext context, 32 | required CodeLineEditingController controller, 33 | required TextSelectionToolbarAnchors anchors, 34 | Rect? renderRect, 35 | required LayerLink layerLink, 36 | required ValueNotifier visibility, 37 | }) { 38 | showMenu( 39 | context: context, 40 | position: RelativeRect.fromSize(anchors.primaryAnchor & const Size(150, double.infinity), 41 | MediaQuery.of(context).size), 42 | items: [ 43 | ContextMenuItemWidget( 44 | text: 'Cut', 45 | onTap: () { 46 | controller.cut(); 47 | }, 48 | ), 49 | ContextMenuItemWidget( 50 | text: 'Copy', 51 | onTap: () { 52 | controller.copy(); 53 | }, 54 | ), 55 | ContextMenuItemWidget( 56 | text: 'Paste', 57 | onTap: () { 58 | controller.paste(); 59 | }, 60 | ), 61 | ] 62 | ); 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /example/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.10) 3 | project(runner LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "example") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "com.example.example") 11 | 12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 13 | # versions of CMake. 14 | cmake_policy(SET CMP0063 NEW) 15 | 16 | # Load bundled libraries from the lib/ directory relative to the binary. 17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 18 | 19 | # Root filesystem for cross-building. 20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 27 | endif() 28 | 29 | # Define build configuration options. 30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 31 | set(CMAKE_BUILD_TYPE "Debug" CACHE 32 | STRING "Flutter build mode" FORCE) 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 34 | "Debug" "Profile" "Release") 35 | endif() 36 | 37 | # Compilation settings that should be applied to most targets. 38 | # 39 | # Be cautious about adding new options here, as plugins use this function by 40 | # default. In most cases, you should add new options to specific targets instead 41 | # of modifying this function. 42 | function(APPLY_STANDARD_SETTINGS TARGET) 43 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 47 | endfunction() 48 | 49 | # Flutter library and tool build rules. 50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 51 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 52 | 53 | # System-level dependencies. 54 | find_package(PkgConfig REQUIRED) 55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 56 | 57 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 58 | 59 | # Define the application target. To change its name, change BINARY_NAME above, 60 | # not the value here, or `flutter run` will no longer work. 61 | # 62 | # Any new source files that you add to the application should be added here. 63 | add_executable(${BINARY_NAME} 64 | "main.cc" 65 | "my_application.cc" 66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 67 | ) 68 | 69 | # Apply the standard set of build settings. This can be removed for applications 70 | # that need different build settings. 71 | apply_standard_settings(${BINARY_NAME}) 72 | 73 | # Add dependency libraries. Add any application-specific dependencies here. 74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 76 | 77 | # Run the Flutter tool portions of the build. This must not be removed. 78 | add_dependencies(${BINARY_NAME} flutter_assemble) 79 | 80 | # Only the install-generated bundle's copy of the executable will launch 81 | # correctly, since the resources must in the right relative locations. To avoid 82 | # people trying to run the unbundled copy, put it in a subdirectory instead of 83 | # the default top-level location. 84 | set_target_properties(${BINARY_NAME} 85 | PROPERTIES 86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 87 | ) 88 | 89 | 90 | # Generated plugin build rules, which manage building the plugins and adding 91 | # them to the application. 92 | include(flutter/generated_plugins.cmake) 93 | 94 | 95 | # === Installation === 96 | # By default, "installing" just makes a relocatable bundle in the build 97 | # directory. 98 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 99 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 100 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 101 | endif() 102 | 103 | # Start with a clean build bundle directory every time. 104 | install(CODE " 105 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 106 | " COMPONENT Runtime) 107 | 108 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 109 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 110 | 111 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 112 | COMPONENT Runtime) 113 | 114 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 115 | COMPONENT Runtime) 116 | 117 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 118 | COMPONENT Runtime) 119 | 120 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) 121 | install(FILES "${bundled_library}" 122 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 123 | COMPONENT Runtime) 124 | endforeach(bundled_library) 125 | 126 | # Copy the native assets provided by the build.dart from all packages. 127 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") 128 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 129 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 130 | COMPONENT Runtime) 131 | 132 | # Fully re-copy the assets directory on each build to avoid having stale files 133 | # from a previous install. 134 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 135 | install(CODE " 136 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 137 | " COMPONENT Runtime) 138 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 139 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 140 | 141 | # Install the AOT library on non-Debug builds only. 142 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 143 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 144 | COMPONENT Runtime) 145 | endif() 146 | -------------------------------------------------------------------------------- /example/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void fl_register_plugins(FlPluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /example/linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "example"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "example"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /example/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | 9 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 10 | } 11 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | characters: 13 | dependency: transitive 14 | description: 15 | name: characters 16 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "1.3.0" 20 | collection: 21 | dependency: "direct main" 22 | description: 23 | name: collection 24 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.18.0" 28 | flutter: 29 | dependency: "direct main" 30 | description: flutter 31 | source: sdk 32 | version: "0.0.0" 33 | flutter_lints: 34 | dependency: "direct dev" 35 | description: 36 | name: flutter_lints 37 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 38 | url: "https://pub.dev" 39 | source: hosted 40 | version: "2.0.3" 41 | isolate_contactor: 42 | dependency: transitive 43 | description: 44 | name: isolate_contactor 45 | sha256: "6ba8434ceb58238a1389d6365111a3efe7baa1c68a66f4db6d63d351cf6c3a0f" 46 | url: "https://pub.dev" 47 | source: hosted 48 | version: "4.1.0" 49 | isolate_manager: 50 | dependency: transitive 51 | description: 52 | name: isolate_manager 53 | sha256: "22ed0c25f80ec3b5f21e3a55d060f4650afff33f27c2dff34c0f9409d5759ae5" 54 | url: "https://pub.dev" 55 | source: hosted 56 | version: "4.1.5+1" 57 | lints: 58 | dependency: transitive 59 | description: 60 | name: lints 61 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 62 | url: "https://pub.dev" 63 | source: hosted 64 | version: "2.1.1" 65 | material_color_utilities: 66 | dependency: transitive 67 | description: 68 | name: material_color_utilities 69 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" 70 | url: "https://pub.dev" 71 | source: hosted 72 | version: "0.5.0" 73 | meta: 74 | dependency: transitive 75 | description: 76 | name: meta 77 | sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e 78 | url: "https://pub.dev" 79 | source: hosted 80 | version: "1.10.0" 81 | path: 82 | dependency: transitive 83 | description: 84 | name: path 85 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 86 | url: "https://pub.dev" 87 | source: hosted 88 | version: "1.9.0" 89 | re_editor: 90 | dependency: "direct main" 91 | description: 92 | path: ".." 93 | relative: true 94 | source: path 95 | version: "0.4.0" 96 | re_highlight: 97 | dependency: "direct main" 98 | description: 99 | name: re_highlight 100 | sha256: "6c4ac3f76f939fb7ca9df013df98526634e17d8f7460e028bd23a035870024f2" 101 | url: "https://pub.dev" 102 | source: hosted 103 | version: "0.0.3" 104 | sky_engine: 105 | dependency: transitive 106 | description: flutter 107 | source: sdk 108 | version: "0.0.99" 109 | stream_channel: 110 | dependency: transitive 111 | description: 112 | name: stream_channel 113 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 114 | url: "https://pub.dev" 115 | source: hosted 116 | version: "2.1.2" 117 | vector_math: 118 | dependency: transitive 119 | description: 120 | name: vector_math 121 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 122 | url: "https://pub.dev" 123 | source: hosted 124 | version: "2.1.4" 125 | web: 126 | dependency: transitive 127 | description: 128 | name: web 129 | sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 130 | url: "https://pub.dev" 131 | source: hosted 132 | version: "0.3.0" 133 | sdks: 134 | dart: ">=3.2.0-194.0.dev <4.0.0" 135 | flutter: ">=1.17.0" 136 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: re_editor_exmaple 2 | description: A new Flutter project. 3 | publish_to: none 4 | 5 | version: 1.0.0 6 | 7 | environment: 8 | sdk: ">=2.17.0 <3.0.0" 9 | flutter: ">=1.17.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | collection: ^1.17.1 15 | re_highlight: ^0.0.2 16 | re_editor: 17 | path: ../ 18 | 19 | dev_dependencies: 20 | flutter_lints: ^2.0.3 21 | 22 | # For information on the generic Dart part of this file, see the 23 | # following page: https://dart.dev/tools/pub/pubspec 24 | 25 | # The following section is specific to Flutter packages. 26 | flutter: 27 | 28 | # The following line ensures that the Material Icons font is 29 | # included with your application, so that you can use the icons in 30 | # the material Icons class. 31 | uses-material-design: true 32 | 33 | # To add assets to your application, add an assets section, like this: 34 | # assets: 35 | # - images/a_dot_burr.jpeg 36 | # - images/a_dot_ham.jpeg 37 | 38 | # An image asset can refer to one or more resolution-specific "variants", see 39 | # https://flutter.dev/assets-and-images/#resolution-aware 40 | 41 | # For details regarding adding assets from package dependencies, see 42 | # https://flutter.dev/assets-and-images/#from-packages 43 | 44 | # To add custom fonts to your application, add a fonts section here, 45 | # in this "flutter" section. Each entry in this list should have a 46 | # "family" key with the font family name, and a "fonts" key with a 47 | # list giving the asset and other descriptors for the font. For 48 | # example: 49 | # fonts: 50 | # - family: Schyler 51 | # fonts: 52 | # - asset: fonts/Schyler-Regular.ttf 53 | # - asset: fonts/Schyler-Italic.ttf 54 | # style: italic 55 | # - family: Trajan Pro 56 | # fonts: 57 | # - asset: fonts/TrajanPro.ttf 58 | # - asset: fonts/TrajanPro_Bold.ttf 59 | # weight: 700 60 | # 61 | # For details regarding fonts from package dependencies, 62 | # see https://flutter.dev/custom-fonts/#from-packages 63 | assets: 64 | - assets/code.json 65 | - assets/code.dart 66 | - assets/large.txt 67 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | example 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(example LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "example") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(VERSION 3.14...3.25) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Copy the native assets provided by the build.dart from all packages. 91 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") 92 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 93 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 94 | COMPONENT Runtime) 95 | 96 | # Fully re-copy the assets directory on each build to avoid having stale files 97 | # from a previous install. 98 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 99 | install(CODE " 100 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 101 | " COMPONENT Runtime) 102 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 103 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 104 | 105 | # Install the AOT library on non-Debug builds only. 106 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 107 | CONFIGURATIONS Profile;Release 108 | COMPONENT Runtime) 109 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void RegisterPlugins(flutter::PluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "example.exe" "\0" 98 | VALUE "ProductName", "example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"example", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reqable/re-editor/8c44d9d75b5cf01fd3c9f31e5116b8a3ac364d27/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | -------------------------------------------------------------------------------- /lib/re_editor.dart: -------------------------------------------------------------------------------- 1 | export 'src/re_editor.dart'; -------------------------------------------------------------------------------- /lib/src/_code_extensions.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | extension _InlineSpanExtension on InlineSpan { 4 | 5 | int get length => _computeLength(); 6 | 7 | int _computeLength() { 8 | int len = 0; 9 | if (this is TextSpan) { 10 | len += (this as TextSpan).length; 11 | } else { 12 | len += toPlainText(includePlaceholders: false).length; 13 | } 14 | return len; 15 | } 16 | 17 | } 18 | 19 | extension _TextSpanExtension on TextSpan { 20 | 21 | int get length => _computeLength(); 22 | 23 | int _computeLength() { 24 | int len = 0; 25 | if (text != null) { 26 | len += text!.length; 27 | } 28 | if (children != null) { 29 | for (final InlineSpan span in children!) { 30 | len += span.length; 31 | } 32 | } 33 | return len; 34 | } 35 | 36 | } 37 | 38 | extension _OffsetExtension on Offset { 39 | 40 | bool isSamePosition(Offset offset) { 41 | return (this - offset).distance < 10; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /lib/src/_code_floating_cursor.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | // The time the animation of the floating cursor snapping to the final cursor position will take. 4 | const Duration floatingCursorSnapDuration = Duration(milliseconds: 300); 5 | 6 | // Class containing the floating cursor control logic. 7 | class _CodeFloatingCursorController 8 | extends ValueNotifier<_FloatingCursorState> { 9 | late final _CodeCursorBlinkController _blinkController; 10 | late final AnimationController _animationController; 11 | 12 | _CodeFloatingCursorController() : super(const _FloatingCursorState()); 13 | 14 | /// Sets the [Offset] and [CodeLineSelection] of the cursors. Setting either one of these offsets 15 | /// to null is equivalent to turning off the corresponding cursor. 16 | void setFloatingCursorPositions( 17 | {Offset? floatingCursorOffset, 18 | Offset? previewCursorOffset, 19 | Offset? finalCursorOffset, 20 | CodeLineSelection? finalCursorSelection}) { 21 | if (value.floatingCursorOffset != null && floatingCursorOffset == null) { 22 | // Starting the floating cursor, stop blinking of the normal cursor 23 | _blinkController.startBlink(); 24 | } else if (value.floatingCursorOffset == null && 25 | floatingCursorOffset != null) { 26 | // Stopping the floating cursor, resume blinking of the normal cursor 27 | _blinkController.stopBlink(); 28 | } 29 | 30 | value = _FloatingCursorState( 31 | floatingCursorOffset: floatingCursorOffset, 32 | previewCursorOffset: previewCursorOffset, 33 | finalCursorOffset: finalCursorOffset, 34 | finalCursorSelection: finalCursorSelection, 35 | ); 36 | } 37 | 38 | /// Turns off the floating cursor by setting all the offsets to null. 39 | void disableFloatingCursor() { 40 | setFloatingCursorPositions(); 41 | } 42 | 43 | /// Update the [Offset] value of preview cursor. 44 | void updatePreviewCursorOffset(Offset? previewCursorOffset) { 45 | value = value.copyWith(previewCursorOffset: previewCursorOffset); 46 | } 47 | 48 | void _onFloatingCursorResetAnimationTick() { 49 | if (_animationController.isCompleted) { 50 | // Once animation is complete, turn off the floating cursor off 51 | disableFloatingCursor(); 52 | } else { 53 | final double lerpValue = _animationController.value; 54 | final double lerpX = ui.lerpDouble(value.floatingCursorOffset!.dx, 55 | value.finalCursorOffset!.dx, lerpValue)!; 56 | final double lerpY = ui.lerpDouble(value.floatingCursorOffset!.dy, 57 | value.finalCursorOffset!.dy, lerpValue)!; 58 | 59 | setFloatingCursorPositions( 60 | floatingCursorOffset: Offset(lerpX, lerpY), 61 | previewCursorOffset: value.previewCursorOffset, 62 | finalCursorOffset: value.finalCursorOffset, 63 | finalCursorSelection: value.finalCursorSelection); 64 | } 65 | } 66 | 67 | /// Performs the "snapping" animation and turns of the floating cursor. 68 | void animateDisableFloatingCursor() { 69 | _animationController.value = 0.0; 70 | _animationController.animateTo(1, 71 | duration: floatingCursorSnapDuration, curve: Curves.decelerate); 72 | } 73 | 74 | @override 75 | void dispose() { 76 | disableFloatingCursor(); 77 | super.dispose(); 78 | } 79 | 80 | /// The two methods below are meant to only be used for property injection. Both [_blinkController] and [_animationController] 81 | /// have to be set before calling any of the other methods for the [_CodeFloatingCursorController] to work properly. 82 | 83 | set blinkController(_CodeCursorBlinkController value) { 84 | _blinkController = value; 85 | } 86 | 87 | set animationController(AnimationController value) { 88 | value.addListener(_onFloatingCursorResetAnimationTick); 89 | _animationController = value; 90 | } 91 | 92 | Offset? get previewCursorOffset => value.previewCursorOffset; 93 | 94 | Offset? get floatingCursorOffset => value.floatingCursorOffset; 95 | } 96 | 97 | class _FloatingCursorState { 98 | /// The offset of the floating cursor. 99 | final Offset? floatingCursorOffset; 100 | 101 | /// The offset of the preview cursor. The preview cursor is only visible when the floating cursor is hovering to the right of 102 | /// the end of the line, where no text is present. It shows where the actual cursor would end up if the floating cursor would 103 | /// be stoped in that moment. 104 | final Offset? previewCursorOffset; 105 | 106 | /// The offset where the actual cursor would be placed if floating cursor would be stopped. 107 | final Offset? finalCursorOffset; 108 | 109 | final CodeLineSelection? finalCursorSelection; 110 | 111 | const _FloatingCursorState( 112 | {this.floatingCursorOffset, 113 | this.previewCursorOffset, 114 | this.finalCursorOffset, 115 | this.finalCursorSelection}); 116 | 117 | /// Creates a copy of this instance with the specified values overridden. 118 | _FloatingCursorState copyWith({ 119 | Offset? floatingCursorOffset, 120 | Offset? previewCursorOffset, 121 | Offset? finalCursorOffset, 122 | CodeLineSelection? finalCursorSelection, 123 | }) { 124 | return _FloatingCursorState( 125 | floatingCursorOffset: floatingCursorOffset ?? this.floatingCursorOffset, 126 | previewCursorOffset: previewCursorOffset ?? this.previewCursorOffset, 127 | finalCursorOffset: finalCursorOffset ?? this.finalCursorOffset, 128 | finalCursorSelection: finalCursorSelection ?? this.finalCursorSelection, 129 | ); 130 | } 131 | 132 | bool isActive() { 133 | // Floating cursor is active only if its offset is not null. 134 | return floatingCursorOffset != null; 135 | } 136 | 137 | @override 138 | bool operator ==(Object other) => 139 | identical(this, other) || 140 | (other is _FloatingCursorState && 141 | floatingCursorOffset == other.floatingCursorOffset && 142 | previewCursorOffset == other.previewCursorOffset && 143 | finalCursorOffset == other.finalCursorOffset && 144 | finalCursorSelection == other.finalCursorSelection); 145 | 146 | @override 147 | int get hashCode => Object.hash(floatingCursorOffset, previewCursorOffset, 148 | finalCursorOffset, finalCursorSelection); 149 | } 150 | -------------------------------------------------------------------------------- /lib/src/_code_lines.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | class _CodeLineSegmentQuckLineCount extends CodeLineSegment { 4 | 5 | late int _lineCount; 6 | 7 | _CodeLineSegmentQuckLineCount({ 8 | required super.codeLines, 9 | required super.dirty, 10 | }) { 11 | _lineCount = super.lineCount; 12 | } 13 | 14 | @override 15 | int get lineCount => _lineCount; 16 | 17 | @override 18 | set length(int newLength) { 19 | super.length = newLength; 20 | _lineCount = super.lineCount; 21 | } 22 | 23 | @override 24 | void add(CodeLine element) { 25 | super.add(element); 26 | _lineCount = super.lineCount; 27 | } 28 | 29 | @override 30 | void operator []=(int index, CodeLine value) { 31 | super[index] = value; 32 | _lineCount = super.lineCount; 33 | } 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /lib/src/_code_scroll.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | const double _kScrollbarThickness = 8.0; 4 | 5 | class _CodeScrollable extends StatelessWidget { 6 | 7 | final AxisDirection axisDirection; 8 | final ScrollController? controller; 9 | final ViewportBuilder viewportBuilder; 10 | final CodeScrollbarBuilder? scrollbarBuilder; 11 | 12 | const _CodeScrollable({ 13 | required this.axisDirection, 14 | this.controller, 15 | required this.viewportBuilder, 16 | this.scrollbarBuilder 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Scrollable( 22 | excludeFromSemantics: true, 23 | controller: controller, 24 | scrollBehavior: _ScrollBehavior(scrollbarBuilder), 25 | viewportBuilder: viewportBuilder, 26 | axisDirection: axisDirection, 27 | physics: const ClampingScrollPhysics(), 28 | ); 29 | } 30 | 31 | } 32 | 33 | class _ScrollBehavior extends MaterialScrollBehavior { 34 | 35 | final _ScrollPhysics physics; 36 | final CodeScrollbarBuilder? scrollbarBuilder; 37 | 38 | _ScrollBehavior(this.scrollbarBuilder) : physics = _ScrollPhysics(); 39 | 40 | @override 41 | Widget buildScrollbar(BuildContext context, Widget child, ScrollableDetails details) { 42 | final Widget? scrollbar = scrollbarBuilder?.call(context, child, details); 43 | if (scrollbar != null) { 44 | return scrollbar; 45 | } 46 | final ScrollbarOrientation? orientation; 47 | if (details.direction == AxisDirection.down) { 48 | orientation = ScrollbarOrientation.right; 49 | } else if (details.direction == AxisDirection.right) { 50 | orientation = ScrollbarOrientation.bottom; 51 | } else { 52 | orientation = null; 53 | } 54 | if (kIsAndroid || kIsIOS) { 55 | return Scrollbar( 56 | controller: details.controller, 57 | scrollbarOrientation: orientation, 58 | thumbVisibility: details.direction == AxisDirection.down, 59 | child: child, 60 | ); 61 | } 62 | return _RawScrollbar( 63 | physics: physics, 64 | controller: details.controller ?? ScrollController(), 65 | scrollbarOrientation: orientation, 66 | thumbVisibility: details.direction == AxisDirection.down, 67 | child: child, 68 | ); 69 | } 70 | 71 | @override 72 | ScrollPhysics getScrollPhysics(BuildContext context) { 73 | return physics; 74 | } 75 | 76 | } 77 | 78 | class _RawScrollbar extends RawScrollbar { 79 | 80 | final _ScrollPhysics physics; 81 | 82 | const _RawScrollbar({ 83 | required this.physics, 84 | required Widget child, 85 | required ScrollController controller, 86 | ScrollbarOrientation? scrollbarOrientation, 87 | required bool thumbVisibility, 88 | }) : super( 89 | controller: controller, 90 | scrollbarOrientation: scrollbarOrientation, 91 | thumbVisibility: thumbVisibility, 92 | thickness: _kScrollbarThickness, 93 | radius: const Radius.circular(10), 94 | crossAxisMargin: 2, 95 | child: child, 96 | ); 97 | 98 | @override 99 | RawScrollbarState<_RawScrollbar> createState() => _RawScrollbarState(); 100 | 101 | } 102 | 103 | class _RawScrollbarState extends RawScrollbarState<_RawScrollbar> { 104 | 105 | Offset? downPosition; 106 | double? downOffset; 107 | 108 | @override 109 | void handleThumbPressStart(ui.Offset localPosition) { 110 | downPosition = localPosition; 111 | downOffset = widget.controller!.offset; 112 | super.handleThumbPressStart(localPosition); 113 | } 114 | 115 | @override 116 | void handleThumbPressUpdate(Offset localPosition) { 117 | if (getScrollbarDirection() == Axis.vertical) { 118 | widget.physics.setScrollPosition(downOffset! + scrollbarPainter.getTrackToScroll(localPosition.dy - downPosition!.dy)); 119 | } 120 | super.handleThumbPressUpdate(localPosition); 121 | } 122 | 123 | } 124 | 125 | // ignore: must_be_immutable 126 | class _ScrollPhysics extends ScrollPhysics { 127 | 128 | double? _position; 129 | 130 | void setScrollPosition(double position) { 131 | _position = position; 132 | } 133 | 134 | @override 135 | double applyBoundaryConditions(ScrollMetrics position, double value) { 136 | if (_position == null) { 137 | return super.applyBoundaryConditions(position, value); 138 | } 139 | return value - _position!; 140 | } 141 | 142 | } -------------------------------------------------------------------------------- /lib/src/_code_span.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | @immutable 4 | class _MouseTrackerAnnotationTextSpan extends TextSpan { 5 | 6 | final int id; 7 | final MouseTrackerAnnotationTextSpan span; 8 | final List rects; 9 | 10 | const _MouseTrackerAnnotationTextSpan({ 11 | required this.id, 12 | required this.rects, 13 | required this.span, 14 | }); 15 | 16 | @override 17 | PointerEnterEventListener? get onEnter => (event) { 18 | span.onEnterWithRect(event, id, rects); 19 | }; 20 | 21 | @override 22 | PointerExitEventListener? get onExit => (event) { 23 | span.onExitWithRect(event, id, rects); 24 | }; 25 | 26 | @override 27 | int get hashCode => span.hashCode; 28 | 29 | @override 30 | bool operator ==(Object other) { 31 | if (identical(this, other)) { 32 | return true; 33 | } 34 | return other is _MouseTrackerAnnotationTextSpan && span == other.span && 35 | id == other.id; 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /lib/src/_consts.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | final kIsMacOS = defaultTargetPlatform == TargetPlatform.macOS; 4 | final kIsAndroid = defaultTargetPlatform == TargetPlatform.android; 5 | final kIsIOS = defaultTargetPlatform == TargetPlatform.iOS; 6 | -------------------------------------------------------------------------------- /lib/src/_isolate.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | typedef IsolateRunnable = Res Function(Req req); 4 | typedef IsolateCallback = void Function(Res res); 5 | 6 | class _IsolateTasker { 7 | final String name; 8 | late bool _closed; 9 | 10 | late IsolateManager? _isolateManager; 11 | 12 | _IsolateTasker(this.name, IsolateRunnable runnable) { 13 | _closed = false; 14 | _isolateManager = IsolateManager.create( 15 | runnable, 16 | concurrent: 1, // one is enough 17 | ); 18 | } 19 | 20 | void run(Req req, IsolateCallback callback) async { 21 | if (_closed) { 22 | return; 23 | } 24 | _isolateManager?.compute(req, callback: (message) async { 25 | if (_closed) { 26 | return false; 27 | } 28 | callback(message); 29 | return true; 30 | }); 31 | } 32 | 33 | void close() { 34 | _closed = true; 35 | _isolateManager?.stop(); 36 | _isolateManager = null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/code_find.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | typedef CodeFindBuilder = PreferredSizeWidget Function(BuildContext context, CodeFindController controller, bool readonly); 4 | 5 | class CodeFindValue { 6 | 7 | final CodeFindOption option; 8 | final bool replaceMode; 9 | final CodeFindResult? result; 10 | final bool searching; 11 | 12 | const CodeFindValue({ 13 | required this.option, 14 | required this.replaceMode, 15 | this.result, 16 | this.searching = false, 17 | }); 18 | 19 | const CodeFindValue.empty() : this( 20 | option: const CodeFindOption( 21 | pattern: '', 22 | caseSensitive: false, 23 | regex: false, 24 | ), 25 | replaceMode: false, 26 | ); 27 | 28 | CodeFindValue copyWith({ 29 | CodeFindOption? option, 30 | bool? replaceMode, 31 | required CodeFindResult? result, 32 | bool? searching, 33 | }) { 34 | return CodeFindValue( 35 | option: option ?? this.option, 36 | replaceMode: replaceMode ?? this.replaceMode, 37 | result: result, 38 | searching: searching ?? this.searching, 39 | ); 40 | } 41 | 42 | @override 43 | bool operator ==(Object other) { 44 | if (identical(this, other)) { 45 | return true; 46 | } 47 | return other is CodeFindValue 48 | && other.option == option 49 | && other.replaceMode == replaceMode 50 | && other.result == result 51 | && other.searching == searching; 52 | } 53 | 54 | @override 55 | int get hashCode => Object.hash(option, replaceMode, result, searching); 56 | 57 | @override 58 | String toString() { 59 | return '{option: $option replaceMode: $replaceMode result: $result searching:$searching}'; 60 | } 61 | 62 | } 63 | 64 | class CodeFindOption { 65 | 66 | final String pattern; 67 | final bool caseSensitive; 68 | final bool regex; 69 | 70 | const CodeFindOption({ 71 | required this.pattern, 72 | required this.caseSensitive, 73 | required this.regex, 74 | }); 75 | 76 | CodeFindOption copyWith({ 77 | String? pattern, 78 | bool? caseSensitive, 79 | bool? regex, 80 | }) { 81 | return CodeFindOption( 82 | pattern: pattern ?? this.pattern, 83 | caseSensitive: caseSensitive ?? this.caseSensitive, 84 | regex: regex ?? this.regex, 85 | ); 86 | } 87 | 88 | RegExp? get regExp { 89 | if (regex) { 90 | try { 91 | return RegExp(pattern, caseSensitive: caseSensitive); 92 | } on FormatException { 93 | return null; 94 | } 95 | } else { 96 | return RegExp(RegExp.escape(pattern), caseSensitive: caseSensitive); 97 | } 98 | } 99 | 100 | @override 101 | bool operator ==(Object other) { 102 | if (identical(this, other)) { 103 | return true; 104 | } 105 | return other is CodeFindOption 106 | && other.pattern == pattern 107 | && other.caseSensitive == caseSensitive 108 | && other.regex == regex; 109 | } 110 | 111 | @override 112 | int get hashCode => Object.hash(pattern, caseSensitive, regex); 113 | 114 | @override 115 | String toString() { 116 | return '{pattern: $pattern caseSensitive: $caseSensitive regex: $regex}'; 117 | } 118 | 119 | } 120 | 121 | class CodeFindResult { 122 | 123 | final int index; 124 | final List matches; 125 | final CodeFindOption option; 126 | final CodeLines codeLines; 127 | final bool dirty; 128 | 129 | const CodeFindResult({ 130 | required this.index, 131 | required this.matches, 132 | required this.option, 133 | required this.codeLines, 134 | required this.dirty, 135 | }); 136 | 137 | CodeFindResult get previous => copyWith( 138 | index: index == 0 ? matches.length - 1 : index - 1 139 | ); 140 | 141 | CodeFindResult get next => copyWith( 142 | index: index == matches.length - 1 ? 0 : index + 1 143 | ); 144 | 145 | CodeLineSelection? get currentMatch => index == -1 ? null : matches[index]; 146 | 147 | CodeFindResult copyWith({ 148 | int? index, 149 | List? matches, 150 | CodeFindOption? option, 151 | CodeLines? codeLines, 152 | bool? dirty, 153 | }) { 154 | return CodeFindResult( 155 | index: index ?? this.index, 156 | matches: matches ?? this.matches, 157 | option: option ?? this.option, 158 | codeLines: codeLines ?? this.codeLines, 159 | dirty: dirty ?? this.dirty, 160 | ); 161 | } 162 | 163 | @override 164 | bool operator ==(Object other) { 165 | if (identical(this, other)) { 166 | return true; 167 | } 168 | return other is CodeFindResult 169 | && other.index == index 170 | && listEquals(other.matches, matches) 171 | && other.option == option 172 | && other.codeLines.equals(codeLines) 173 | && other.dirty == dirty; 174 | } 175 | 176 | @override 177 | int get hashCode => Object.hash(index, matches, option, codeLines, dirty); 178 | 179 | } 180 | 181 | abstract class CodeFindController extends ValueNotifier { 182 | 183 | factory CodeFindController(CodeLineEditingController controller, [CodeFindValue? value]) 184 | => _CodeFindControllerImpl(controller, value); 185 | 186 | TextEditingController get findInputController; 187 | 188 | TextEditingController get replaceInputController; 189 | 190 | FocusNode get findInputFocusNode; 191 | 192 | FocusNode get replaceInputFocusNode; 193 | 194 | List? get allMatchSelections; 195 | 196 | CodeLineSelection? get currentMatchSelection; 197 | 198 | void findMode(); 199 | 200 | void replaceMode(); 201 | 202 | void focusOnFindInput(); 203 | 204 | void focusOnReplaceInput(); 205 | 206 | void toggleMode(); 207 | 208 | void close(); 209 | 210 | void toggleRegex(); 211 | 212 | void toggleCaseSensitive(); 213 | 214 | void previousMatch(); 215 | 216 | void nextMatch(); 217 | 218 | void replaceMatch(); 219 | 220 | void replaceAllMatches(); 221 | 222 | CodeLineSelection? convertMatchToSelection(CodeLineSelection match); 223 | 224 | } -------------------------------------------------------------------------------- /lib/src/code_formatter.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | abstract class CodeCommentFormatter { 4 | 5 | CodeLineEditingValue format(CodeLineEditingValue value, String indent, bool single); 6 | 7 | } 8 | 9 | abstract class DefaultCodeCommentFormatter implements CodeCommentFormatter { 10 | 11 | factory DefaultCodeCommentFormatter({ 12 | String? singleLinePrefix, 13 | String? multiLinePrefix, 14 | String? multiLineSuffix 15 | }) => _DefaultCodeCommentFormatter( 16 | singleLinePrefix: singleLinePrefix, 17 | multiLinePrefix: multiLinePrefix, 18 | multiLineSuffix: multiLineSuffix 19 | ); 20 | 21 | } -------------------------------------------------------------------------------- /lib/src/code_indicator.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | const int _kDefaultMinNumberCount = 3; 4 | const Size _kDefaultChunkIndicatorSize = Size(7, 7); 5 | 6 | typedef CodeIndicatorValueNotifier = ValueNotifier; 7 | typedef CodeIndicatorBuilder = Widget Function( 8 | BuildContext context, 9 | CodeLineEditingController editingController, 10 | CodeChunkController chunkController, 11 | CodeIndicatorValueNotifier notifier, 12 | ); 13 | 14 | class CodeIndicatorValue { 15 | 16 | final List paragraphs; 17 | final int focusedIndex; 18 | 19 | CodeIndicatorValue({ 20 | required this.paragraphs, 21 | this.focusedIndex = -1, 22 | }); 23 | 24 | @override 25 | int get hashCode => Object.hashAll([paragraphs, focusedIndex]); 26 | 27 | @override 28 | bool operator ==(Object other) { 29 | if (other is! CodeIndicatorValue) { 30 | return false; 31 | } 32 | return listEquals(other.paragraphs, paragraphs) && 33 | other.focusedIndex == focusedIndex; 34 | } 35 | 36 | CodeIndicatorValue copyWith({ 37 | List? paragraphs, 38 | int? focusedIndex, 39 | }) { 40 | return CodeIndicatorValue( 41 | paragraphs: paragraphs ?? this.paragraphs, 42 | focusedIndex: focusedIndex ?? this.focusedIndex, 43 | ); 44 | } 45 | 46 | } 47 | 48 | class DefaultCodeLineNumber extends LeafRenderObjectWidget { 49 | 50 | final CodeLineEditingController controller; 51 | final CodeIndicatorValueNotifier notifier; 52 | final TextStyle? textStyle; 53 | final TextStyle? focusedTextStyle; 54 | final int? minNumberCount; 55 | final String Function(int lineIndex)? customLineIndex2Text; 56 | 57 | const DefaultCodeLineNumber({ 58 | super.key, 59 | required this.notifier, 60 | required this.controller, 61 | this.textStyle, 62 | this.focusedTextStyle, 63 | this.minNumberCount, 64 | this.customLineIndex2Text, 65 | }); 66 | 67 | @override 68 | RenderObject createRenderObject(BuildContext context) => CodeLineNumberRenderObject( 69 | controller: controller, 70 | notifier: notifier, 71 | textStyle: textStyle ?? _useCodeTextStyle(context, false), 72 | focusedTextStyle: focusedTextStyle ?? _useCodeTextStyle(context, true), 73 | minNumberCount: minNumberCount ?? _kDefaultMinNumberCount, 74 | custonLineIndex2Text: customLineIndex2Text, 75 | ); 76 | 77 | @override 78 | void updateRenderObject(BuildContext context, covariant CodeLineNumberRenderObject renderObject) { 79 | renderObject 80 | ..controller = controller 81 | ..notifier = notifier 82 | ..textStyle = textStyle ?? _useCodeTextStyle(context, false) 83 | ..focusedTextStyle = focusedTextStyle ?? _useCodeTextStyle(context, true) 84 | ..minNumberCount = minNumberCount ?? _kDefaultMinNumberCount; 85 | super.updateRenderObject(context, renderObject); 86 | } 87 | 88 | TextStyle _useCodeTextStyle(BuildContext context, bool focused) { 89 | final _CodeEditable? editor = context.findAncestorWidgetOfExactType<_CodeEditable>(); 90 | assert(editor != null); 91 | return editor!.textStyle.copyWith( 92 | color: focused ? null : editor.textStyle.color?.withAlpha(128) 93 | ); 94 | } 95 | 96 | } 97 | 98 | class DefaultCodeChunkIndicator extends LeafRenderObjectWidget { 99 | 100 | final double width; 101 | final CodeChunkController controller; 102 | final CodeIndicatorValueNotifier notifier; 103 | final CodeChunkIndicatorPainter? painter; 104 | final bool collapseIndicatorVisible; 105 | final bool expandIndicatorVisible; 106 | 107 | const DefaultCodeChunkIndicator({ 108 | super.key, 109 | required this.width, 110 | required this.controller, 111 | required this.notifier, 112 | this.painter, 113 | this.collapseIndicatorVisible = true, 114 | this.expandIndicatorVisible = true, 115 | }); 116 | 117 | @override 118 | RenderObject createRenderObject(BuildContext context) => CodeChunkIndicatorRenderObject( 119 | width: width, 120 | controller: controller, 121 | notifier: notifier, 122 | painter: painter ?? DefaultCodeChunkIndicatorPainter( 123 | color: _useCodeTextColor(context), 124 | ), 125 | collapseIndicatorVisible: collapseIndicatorVisible, 126 | expandIndicatorVisible: expandIndicatorVisible, 127 | ); 128 | 129 | @override 130 | void updateRenderObject(BuildContext context, covariant CodeChunkIndicatorRenderObject renderObject) { 131 | renderObject 132 | ..width = width 133 | ..controller = controller 134 | ..notifier = notifier 135 | ..painter = painter ?? DefaultCodeChunkIndicatorPainter( 136 | color: _useCodeTextColor(context), 137 | ) 138 | ..collapseIndicatorVisible = collapseIndicatorVisible 139 | ..expandIndicatorVisible = expandIndicatorVisible; 140 | super.updateRenderObject(context, renderObject); 141 | } 142 | 143 | Color? _useCodeTextColor(BuildContext context) { 144 | final _CodeEditable? editor = context.findAncestorWidgetOfExactType<_CodeEditable>(); 145 | assert(editor != null); 146 | return editor!.textStyle.color?.withAlpha(128); 147 | } 148 | 149 | } 150 | 151 | abstract class CodeChunkIndicatorPainter { 152 | 153 | void paintCollapseIndicator(Canvas canvas, Size container); 154 | 155 | void paintExpandIndicator(Canvas canvas, Size container); 156 | 157 | } 158 | 159 | class DefaultCodeChunkIndicatorPainter implements CodeChunkIndicatorPainter { 160 | 161 | final Color? color; 162 | final Size size; 163 | 164 | late final Paint _paint; 165 | 166 | DefaultCodeChunkIndicatorPainter({ 167 | this.color, 168 | this.size = _kDefaultChunkIndicatorSize 169 | }) { 170 | _paint = Paint(); 171 | if (color != null) { 172 | _paint.color = color!; 173 | } 174 | } 175 | 176 | @override 177 | void paintCollapseIndicator(Canvas canvas, Size container) { 178 | if (color == null || color == Colors.transparent || container.isEmpty) { 179 | return; 180 | } 181 | final Path path = Path(); 182 | path.moveTo((container.width - size.width) / 2, (container.height - size.height) / 2); 183 | path.lineTo((container.width + size.width) / 2, (container.height - size.height) / 2); 184 | path.lineTo(container.width / 2, (container.height + size.height) / 2); 185 | path.lineTo((container.width - size.width) / 2, (container.height - size.height) / 2); 186 | canvas.drawPath(path, _paint); 187 | } 188 | 189 | @override 190 | void paintExpandIndicator(Canvas canvas, Size container) { 191 | if (color == null || color == Colors.transparent || container.isEmpty) { 192 | return; 193 | } 194 | final Path path = Path(); 195 | path.moveTo((container.width - size.width) / 2, (container.height - size.height) / 2); 196 | path.lineTo((container.width + size.width) / 2, container.height / 2); 197 | path.lineTo((container.width - size.width) / 2, (container.height + size.height) / 2); 198 | path.lineTo((container.width - size.width) / 2, (container.height - size.height) / 2); 199 | canvas.drawPath(path, _paint); 200 | } 201 | 202 | } 203 | -------------------------------------------------------------------------------- /lib/src/code_paragraph.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | abstract class IParagraph { 4 | 5 | double get width; 6 | double get height; 7 | double get preferredLineHeight; 8 | bool get trucated; 9 | int get length; 10 | int get lineCount; 11 | 12 | void draw(Canvas canvas, Offset offset); 13 | 14 | TextPosition getPosition(Offset offset); 15 | 16 | TextRange getWord(Offset offset); 17 | 18 | InlineSpan? getSpanForPosition(TextPosition position); 19 | 20 | TextRange getRangeForSpan(InlineSpan span); 21 | 22 | TextRange getLineBoundary(TextPosition position); 23 | 24 | Offset? getOffset(TextPosition position); 25 | 26 | List getRangeRects(TextRange range); 27 | 28 | } -------------------------------------------------------------------------------- /lib/src/code_scroll.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | typedef CodeScrollbarBuilder = Widget Function(BuildContext context, Widget child, ScrollableDetails details); 4 | 5 | class CodeScrollController { 6 | 7 | final ScrollController verticalScroller; 8 | final ScrollController horizontalScroller; 9 | 10 | GlobalKey? _editorKey; 11 | 12 | CodeScrollController({ 13 | ScrollController? verticalScroller, 14 | ScrollController? horizontalScroller, 15 | }) : verticalScroller = verticalScroller ?? ScrollController(), 16 | horizontalScroller = horizontalScroller ?? ScrollController(); 17 | 18 | void makeCenterIfInvisible(CodeLinePosition position) { 19 | _render?.makePositionCenterIfInvisible(position); 20 | } 21 | 22 | void makeVisible(CodeLinePosition position) { 23 | _render?.makePositionVisible(position); 24 | } 25 | 26 | void bindEditor(GlobalKey key) { 27 | _editorKey = key; 28 | } 29 | 30 | _CodeFieldRender? get _render => _editorKey?.currentContext?.findRenderObject() as _CodeFieldRender?; 31 | 32 | void dispose() { 33 | _editorKey = null; 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /lib/src/code_span.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | typedef PointerEnterEventWithRectListener = void Function(PointerEnterEvent event, int id, List rects); 4 | 5 | typedef PointerExitEventWithRectListener = void Function(PointerExitEvent event, int id, List rects); 6 | 7 | @immutable 8 | class MouseTrackerAnnotationTextSpan extends TextSpan { 9 | 10 | final PointerEnterEventWithRectListener onEnterWithRect; 11 | final PointerExitEventWithRectListener onExitWithRect; 12 | 13 | const MouseTrackerAnnotationTextSpan({ 14 | super.text, 15 | super.children, 16 | super.style, 17 | super.recognizer, 18 | super.mouseCursor, 19 | super.semanticsLabel, 20 | super.locale, 21 | super.spellOut, 22 | required this.onEnterWithRect, 23 | required this.onExitWithRect, 24 | }); 25 | 26 | } -------------------------------------------------------------------------------- /lib/src/code_theme.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | /// The code syntax highlighting theme. We use Re-Highlight as the syntax highlighting rule. 4 | /// Re-Highlight provides some built-in syntax highlighting rules for dozens of programming languages, 5 | /// and many theme styles. 6 | /// 7 | /// Please see [Re-Highlight](https://reqable/re-highlight). 8 | class CodeHighlightTheme { 9 | 10 | const CodeHighlightTheme({ 11 | required this.languages, 12 | required this.theme, 13 | this.plugins = const [], 14 | }); 15 | 16 | /// Supported syntax highlighting language rules. 17 | final Map languages; 18 | 19 | /// The syntax highlighting style theme. 20 | final Map theme; 21 | 22 | /// The plugins for syntax highlighting. 23 | final List plugins; 24 | 25 | @override 26 | int get hashCode => Object.hash(languages, theme, plugins); 27 | 28 | @override 29 | bool operator ==(Object other) { 30 | if (identical(this, other)) { 31 | return true; 32 | } 33 | return other is CodeHighlightTheme 34 | && mapEquals(other.languages, languages) 35 | && mapEquals(other.theme, theme) 36 | && listEquals(other.plugins, plugins); 37 | } 38 | 39 | } 40 | 41 | /// Define language rules and restrictions for highlighting. 42 | /// 43 | /// Since the dart stack capacity is relatively small, stack overflow may occur 44 | /// when executing syntax highlighting, so we added some restrictions to disable 45 | /// syntax highlighting. 46 | /// 47 | /// The issue see https://github.com/dart-lang/sdk/issues/48425 48 | class CodeHighlightThemeMode { 49 | 50 | const CodeHighlightThemeMode({ 51 | required this.mode, 52 | this.maxSize = 4 * 1024 * 1024, 53 | this.maxLineLength = 1 * 1024 * 1024, 54 | }); 55 | 56 | /// Syntax highlighting language rule. 57 | final Mode mode; 58 | 59 | /// If the total text length is higher than this value, syntax highlighting 60 | /// will not be performed. 61 | /// 62 | /// The default value is 4MB. 63 | final int maxSize; 64 | 65 | /// If the text length of a line is higher than this value, syntax highlighting 66 | /// will not be performed. 67 | /// 68 | /// The default value is 1MB. 69 | final int maxLineLength; 70 | 71 | @override 72 | int get hashCode => Object.hash(mode, maxSize, maxLineLength); 73 | 74 | @override 75 | bool operator ==(Object other) { 76 | if (identical(this, other)) { 77 | return true; 78 | } 79 | return other is CodeHighlightThemeMode 80 | && mode == other.mode && maxSize == other.maxSize 81 | && maxLineLength == other.maxLineLength; 82 | } 83 | 84 | } 85 | 86 | extension CodeHighlightThemeModeExtension on Mode { 87 | 88 | CodeHighlightThemeMode get themeMode => CodeHighlightThemeMode(mode: this); 89 | 90 | } -------------------------------------------------------------------------------- /lib/src/code_toolbar.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | typedef ToolbarMenuBuilder = Widget Function({ 4 | required BuildContext context, 5 | required TextSelectionToolbarAnchors anchors, 6 | required CodeLineEditingController controller, 7 | required VoidCallback onDismiss, 8 | required VoidCallback onRefresh, 9 | }); 10 | 11 | abstract class SelectionToolbarController { 12 | 13 | void show({ 14 | required BuildContext context, 15 | required CodeLineEditingController controller, 16 | required TextSelectionToolbarAnchors anchors, 17 | Rect? renderRect, 18 | required LayerLink layerLink, 19 | required ValueNotifier visibility, 20 | }); 21 | 22 | void hide(BuildContext context); 23 | 24 | } 25 | 26 | abstract class MobileSelectionToolbarController implements SelectionToolbarController { 27 | 28 | factory MobileSelectionToolbarController({ 29 | required ToolbarMenuBuilder builder 30 | }) => _MobileSelectionToolbarController( 31 | builder: builder 32 | ); 33 | 34 | } -------------------------------------------------------------------------------- /lib/src/debug/_trace.dart: -------------------------------------------------------------------------------- 1 | part of re_editor; 2 | 3 | class _Trace { 4 | 5 | static final Map _timestamps = {}; 6 | 7 | static void begin(String name) { 8 | _timestamps[name] = DateTime.now(); 9 | } 10 | 11 | static void end(String name, [bool microsecond = false]) { 12 | final DateTime? time = _timestamps.remove(name); 13 | if (time != null) { 14 | if (microsecond) { 15 | print('[${DateTime.now()}] $name costs ${DateTime.now().microsecondsSinceEpoch - time.microsecondsSinceEpoch} us'); 16 | } else { 17 | print('[${DateTime.now()}] $name costs ${DateTime.now().millisecondsSinceEpoch - time.millisecondsSinceEpoch} ms'); 18 | } 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /lib/src/re_editor.dart: -------------------------------------------------------------------------------- 1 | library re_editor; 2 | 3 | import 'dart:async'; 4 | import 'dart:math'; 5 | import 'dart:ui' as ui; 6 | import 'dart:collection'; 7 | 8 | import 'package:flutter/cupertino.dart'; 9 | import 'package:flutter/foundation.dart'; 10 | import 'package:flutter/gestures.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/rendering.dart'; 13 | import 'package:flutter/scheduler.dart'; 14 | import 'package:flutter/services.dart'; 15 | 16 | import 'package:re_highlight/re_highlight.dart'; 17 | import 'package:isolate_manager/isolate_manager.dart'; 18 | 19 | part '_code_floating_cursor.dart'; 20 | part '_code_autocomplete.dart'; 21 | part '_code_editable.dart'; 22 | part '_code_extensions.dart'; 23 | part '_code_field.dart'; 24 | part '_code_find.dart'; 25 | part '_code_formatter.dart'; 26 | part '_code_highlight.dart'; 27 | part '_code_indicator.dart'; 28 | part '_code_input.dart'; 29 | part '_code_line.dart'; 30 | part '_code_lines.dart'; 31 | part '_code_paragraph.dart'; 32 | part '_code_scroll.dart'; 33 | part '_code_selection.dart'; 34 | part '_code_shortcuts.dart'; 35 | part '_code_span.dart'; 36 | part '_consts.dart'; 37 | part '_isolate.dart'; 38 | part 'code_autocomplete.dart'; 39 | part 'code_chunk.dart'; 40 | part 'code_editor.dart'; 41 | part 'code_find.dart'; 42 | part 'code_formatter.dart'; 43 | part 'code_indicator.dart'; 44 | part 'code_line.dart'; 45 | part 'code_lines.dart'; 46 | part 'code_paragraph.dart'; 47 | part 'code_shortcuts.dart'; 48 | part 'code_scroll.dart'; 49 | part 'code_span.dart'; 50 | part 'code_theme.dart'; 51 | part 'code_toolbar.dart'; 52 | part 'debug/_trace.dart'; -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: re_editor 2 | description: Re-Editor is a powerful lightweight text and code editor widget. 3 | version: 0.7.0 4 | homepage: https://reqable.com 5 | repository: https://github.com/reqable/re-editor 6 | 7 | environment: 8 | sdk: ">=2.17.3 <4.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | re_highlight: ^0.0.3 14 | isolate_manager: ^4.1.5+1 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | flutter_lints: ^2.0.1 20 | path: ^1.8.2 -------------------------------------------------------------------------------- /test/code_line_editing_value_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter_test/flutter_test.dart'; 4 | import 'package:re_editor/re_editor.dart'; 5 | 6 | void main() { 7 | group('CodeLineEditingValue constructor ', () { 8 | test('`CodeLineEditingValue()`', () { 9 | { 10 | final CodeLineEditingValue value = CodeLineEditingValue( 11 | codeLines: CodeLines.of(const [ 12 | CodeLine('abc') 13 | ]) 14 | ); 15 | expect(value.codeLines, CodeLines.of(const [ 16 | CodeLine('abc') 17 | ])); 18 | expect(value.selection, const CodeLineSelection.zero()); 19 | expect(value.composing, TextRange.empty); 20 | } 21 | { 22 | final CodeLineEditingValue value = CodeLineEditingValue( 23 | codeLines: CodeLines.of(const [ 24 | CodeLine('abc') 25 | ]), 26 | selection: const CodeLineSelection.collapsed( 27 | index: 0, 28 | offset: 1 29 | ), 30 | composing: const TextRange( 31 | start: 1, 32 | end: 2 33 | ) 34 | ); 35 | expect(value.codeLines, CodeLines.of(const [ 36 | CodeLine('abc') 37 | ])); 38 | expect(value.selection, const CodeLineSelection.collapsed( 39 | index: 0, 40 | offset: 1 41 | )); 42 | expect(value.composing, const TextRange( 43 | start: 1, 44 | end: 2 45 | )); 46 | } 47 | }); 48 | 49 | test('`CodeLineEditingValue.empty()`', () { 50 | const CodeLineEditingValue value = CodeLineEditingValue.empty(); 51 | expect(value.codeLines, CodeLines.of(const [ 52 | CodeLine.empty 53 | ])); 54 | expect(value.selection, const CodeLineSelection.zero()); 55 | expect(value.composing, TextRange.empty); 56 | }); 57 | }); 58 | 59 | group('CodeLineEditingValue method ', () { 60 | test('`copyWith()`', () { 61 | final CodeLineEditingValue value = CodeLineEditingValue( 62 | codeLines: CodeLines.of(const [ 63 | CodeLine('abc') 64 | ]), 65 | selection: const CodeLineSelection.collapsed( 66 | index: 0, 67 | offset: 1 68 | ), 69 | composing: const TextRange( 70 | start: 1, 71 | end: 2 72 | ) 73 | ); 74 | expect(value.copyWith(), value); 75 | expect(value.copyWith( 76 | codeLines: CodeLines.of(const [ 77 | CodeLine('foo'), 78 | CodeLine('bar') 79 | ]) 80 | ), CodeLineEditingValue( 81 | codeLines: CodeLines.of(const [ 82 | CodeLine('foo'), 83 | CodeLine('bar') 84 | ]), 85 | selection: const CodeLineSelection.collapsed( 86 | index: 0, 87 | offset: 1 88 | ), 89 | composing: const TextRange( 90 | start: 1, 91 | end: 2 92 | ) 93 | )); 94 | expect(value.copyWith( 95 | selection: const CodeLineSelection.collapsed( 96 | index: 0, 97 | offset: 2 98 | ), 99 | ), CodeLineEditingValue( 100 | codeLines: CodeLines.of(const [ 101 | CodeLine('abc') 102 | ]), 103 | selection: const CodeLineSelection.collapsed( 104 | index: 0, 105 | offset: 2 106 | ), 107 | composing: const TextRange( 108 | start: 1, 109 | end: 2 110 | ) 111 | )); 112 | expect(value.copyWith( 113 | composing: const TextRange( 114 | start: 2, 115 | end: 3 116 | ) 117 | ), CodeLineEditingValue( 118 | codeLines: CodeLines.of(const [ 119 | CodeLine('abc') 120 | ]), 121 | selection: const CodeLineSelection.collapsed( 122 | index: 0, 123 | offset: 1 124 | ), 125 | composing: const TextRange( 126 | start: 2, 127 | end: 3 128 | ) 129 | )); 130 | }); 131 | }); 132 | 133 | group('CodeLineEditingValue operator ', () { 134 | test('`==`', () { 135 | { 136 | final CodeLineEditingValue value1 = CodeLineEditingValue( 137 | codeLines: CodeLines.of(const [ 138 | CodeLine('abc') 139 | ]), 140 | selection: const CodeLineSelection.collapsed( 141 | index: 0, 142 | offset: 0 143 | ), 144 | composing: const TextRange( 145 | start: 0, 146 | end: 0 147 | ) 148 | ); 149 | final CodeLineEditingValue value2 = CodeLineEditingValue( 150 | codeLines: CodeLines.of(const [ 151 | CodeLine('abc') 152 | ]), 153 | selection: const CodeLineSelection.collapsed( 154 | index: 0, 155 | offset: 0 156 | ), 157 | composing: const TextRange( 158 | start: 0, 159 | end: 0 160 | ) 161 | ); 162 | expect(value1, value2); 163 | } 164 | { 165 | final CodeLineEditingValue value1 = CodeLineEditingValue( 166 | codeLines: CodeLines.of(const [ 167 | CodeLine('abc') 168 | ]), 169 | selection: const CodeLineSelection.collapsed( 170 | index: 0, 171 | offset: 0 172 | ), 173 | composing: const TextRange( 174 | start: 0, 175 | end: 0 176 | ) 177 | ); 178 | final CodeLineEditingValue value2 = CodeLineEditingValue( 179 | codeLines: CodeLines.of(const [ 180 | CodeLine('foobar') 181 | ]), 182 | selection: const CodeLineSelection.collapsed( 183 | index: 0, 184 | offset: 0 185 | ), 186 | composing: const TextRange( 187 | start: 0, 188 | end: 0 189 | ) 190 | ); 191 | expect(value1 != value2, true); 192 | } 193 | { 194 | final CodeLineEditingValue value1 = CodeLineEditingValue( 195 | codeLines: CodeLines.of(const [ 196 | CodeLine('abc') 197 | ]), 198 | selection: const CodeLineSelection.collapsed( 199 | index: 0, 200 | offset: 0 201 | ), 202 | composing: const TextRange( 203 | start: 0, 204 | end: 0 205 | ) 206 | ); 207 | final CodeLineEditingValue value2 = CodeLineEditingValue( 208 | codeLines: CodeLines.of(const [ 209 | CodeLine('abc') 210 | ]), 211 | selection: const CodeLineSelection.collapsed( 212 | index: 0, 213 | offset: 1 214 | ), 215 | composing: const TextRange( 216 | start: 0, 217 | end: 0 218 | ) 219 | ); 220 | expect(value1 != value2, true); 221 | } 222 | { 223 | final CodeLineEditingValue value1 = CodeLineEditingValue( 224 | codeLines: CodeLines.of(const [ 225 | CodeLine('abc') 226 | ]), 227 | selection: const CodeLineSelection.collapsed( 228 | index: 0, 229 | offset: 0 230 | ), 231 | composing: const TextRange( 232 | start: 0, 233 | end: 0 234 | ) 235 | ); 236 | final CodeLineEditingValue value2 = CodeLineEditingValue( 237 | codeLines: CodeLines.of(const [ 238 | CodeLine('abc') 239 | ]), 240 | selection: const CodeLineSelection.collapsed( 241 | index: 0, 242 | offset: 0 243 | ), 244 | composing: const TextRange( 245 | start: 1, 246 | end: 0 247 | ) 248 | ); 249 | expect(value1 != value2, true); 250 | } 251 | }); 252 | }); 253 | } -------------------------------------------------------------------------------- /test/code_line_position_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:re_editor/re_editor.dart'; 4 | 5 | void main() { 6 | group('CodeLinePosition constructor ', () { 7 | test('`CodeLinePosition()`', () { 8 | const CodeLinePosition position = CodeLinePosition( 9 | index: 1, 10 | offset: 2, 11 | affinity: TextAffinity.upstream 12 | ); 13 | expect(position.index, 1); 14 | expect(position.offset, 2); 15 | expect(position.affinity, TextAffinity.upstream); 16 | }); 17 | test('`CodeLinePosition.from()`', () { 18 | final CodeLinePosition position = CodeLinePosition.from( 19 | index: 1, 20 | position: const TextPosition( 21 | offset: 2, 22 | affinity: TextAffinity.upstream 23 | ) 24 | ); 25 | expect(position.index, 1); 26 | expect(position.offset, 2); 27 | expect(position.affinity, TextAffinity.upstream); 28 | }); 29 | }); 30 | 31 | group('CodeLinePosition method ', () { 32 | test('`copyWith`', () { 33 | const CodeLinePosition position = CodeLinePosition( 34 | index: 1, 35 | offset: 2, 36 | affinity: TextAffinity.downstream 37 | ); 38 | expect(position.copyWith(), position); 39 | expect(position.copyWith( 40 | index: 0 41 | ), const CodeLinePosition( 42 | index: 0, 43 | offset: 2, 44 | affinity: TextAffinity.downstream 45 | )); 46 | expect(position.copyWith( 47 | offset: 0 48 | ), const CodeLinePosition( 49 | index: 1, 50 | offset: 0, 51 | affinity: TextAffinity.downstream 52 | )); 53 | expect(position.copyWith( 54 | affinity: TextAffinity.upstream 55 | ), const CodeLinePosition( 56 | index: 1, 57 | offset: 2, 58 | affinity: TextAffinity.upstream 59 | )); 60 | expect(position.copyWith( 61 | index: 0, 62 | offset: 0, 63 | affinity: TextAffinity.downstream 64 | ), const CodeLinePosition( 65 | index: 0, 66 | offset: 0, 67 | affinity: TextAffinity.downstream 68 | )); 69 | }); 70 | }); 71 | 72 | group('CodeLinePosition operator ', () { 73 | test('`==`', () { 74 | { 75 | final CodeLinePosition position1 = CodeLinePosition.from( 76 | index: 1, 77 | position: const TextPosition( 78 | offset: 2, 79 | affinity: TextAffinity.upstream 80 | ) 81 | ); 82 | final CodeLinePosition position2 = CodeLinePosition.from( 83 | index: 1, 84 | position: const TextPosition( 85 | offset: 2, 86 | affinity: TextAffinity.upstream 87 | ) 88 | ); 89 | expect(position1, position2); 90 | } 91 | { 92 | final CodeLinePosition position1 = CodeLinePosition.from( 93 | index: 1, 94 | position: const TextPosition( 95 | offset: 1, 96 | affinity: TextAffinity.upstream 97 | ) 98 | ); 99 | final CodeLinePosition position2 = CodeLinePosition.from( 100 | index: 2, 101 | position: const TextPosition( 102 | offset: 2, 103 | affinity: TextAffinity.upstream 104 | ) 105 | ); 106 | expect(position1 == position2, false); 107 | } 108 | }); 109 | }); 110 | } -------------------------------------------------------------------------------- /test/code_line_range_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:re_editor/re_editor.dart'; 4 | 5 | void main() { 6 | group('CodeLineRange constructor ', () { 7 | test('`CodeLineRange()`', () { 8 | const CodeLineRange range = CodeLineRange( 9 | index: 1, 10 | start: 2, 11 | end: 3 12 | ); 13 | expect(range.index, 1); 14 | expect(range.start, 2); 15 | expect(range.end, 3); 16 | }); 17 | 18 | test('`CodeLineRange.from()`', () { 19 | final CodeLineRange range = CodeLineRange.from( 20 | index: 1, 21 | range: const TextRange( 22 | start: 2, 23 | end: 3 24 | ) 25 | ); 26 | expect(range.index, 1); 27 | expect(range.start, 2); 28 | expect(range.end, 3); 29 | }); 30 | 31 | test('`CodeLineRange.collapsed()`', () { 32 | const CodeLineRange range = CodeLineRange.collapsed( 33 | index: 1, 34 | offset: 2 35 | ); 36 | expect(range.index, 1); 37 | expect(range.start, 2); 38 | expect(range.end, 2); 39 | }); 40 | 41 | test('`CodeLineRange.empty()`', () { 42 | const CodeLineRange range = CodeLineRange.empty(); 43 | expect(range.index, 0); 44 | expect(range.start, -1); 45 | expect(range.end, -1); 46 | }); 47 | }); 48 | 49 | group('CodeLineRange method ', () { 50 | test('`copyWith`', () { 51 | const CodeLineRange range = CodeLineRange( 52 | index: 1, 53 | start: 2, 54 | end: 3 55 | ); 56 | expect(range.copyWith(), range); 57 | expect(range.copyWith( 58 | index: 0 59 | ), const CodeLineRange( 60 | index: 0, 61 | start: 2, 62 | end: 3 63 | )); 64 | expect(range.copyWith( 65 | start: 0 66 | ), const CodeLineRange( 67 | index: 1, 68 | start: 0, 69 | end: 3 70 | )); 71 | expect(range.copyWith( 72 | end: 0 73 | ), const CodeLineRange( 74 | index: 1, 75 | start: 2, 76 | end: 0 77 | )); 78 | expect(range.copyWith( 79 | index: 0, 80 | start: 0, 81 | end: 0 82 | ), const CodeLineRange( 83 | index: 0, 84 | start: 0, 85 | end: 0 86 | )); 87 | }); 88 | }); 89 | 90 | group('CodeLineRange operator ', () { 91 | test('`==`', () { 92 | { 93 | final CodeLineRange range1 = CodeLineRange.from( 94 | index: 1, 95 | range: const TextRange( 96 | start: 2, 97 | end: 3 98 | ) 99 | ); 100 | final CodeLineRange range2 = CodeLineRange.from( 101 | index: 1, 102 | range: const TextRange( 103 | start: 2, 104 | end: 3 105 | ) 106 | ); 107 | expect(range1, range2); 108 | } 109 | { 110 | final CodeLineRange range1 = CodeLineRange.from( 111 | index: 1, 112 | range: const TextRange( 113 | start: 2, 114 | end: 3 115 | ) 116 | ); 117 | final CodeLineRange range2 = CodeLineRange.from( 118 | index: 2, 119 | range: const TextRange( 120 | start: 2, 121 | end: 3 122 | ) 123 | ); 124 | expect(range1 == range2, false); 125 | } 126 | }); 127 | }); 128 | } -------------------------------------------------------------------------------- /test/code_line_utils_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:re_editor/re_editor.dart'; 3 | 4 | void main() { 5 | group('CodeLineUtils', () { 6 | test('toTextLinesSync', () { 7 | expect(CodeLineUtils.toTextLines(''), const [ 8 | '' 9 | ]); 10 | expect(CodeLineUtils.toTextLines('abc'), const [ 11 | 'abc' 12 | ]); 13 | expect(CodeLineUtils.toTextLines('abc\nfoo\nbar'), const [ 14 | 'abc', 15 | 'foo', 16 | 'bar' 17 | ]); 18 | expect(CodeLineUtils.toTextLines('abc\r\nfoo\rbar'), const [ 19 | 'abc', 20 | 'foo', 21 | 'bar' 22 | ]); 23 | }); 24 | test('toCodeLinesSync', () { 25 | expect(CodeLineUtils.toCodeLines('abc\nfoo\nbar'), CodeLines.of(const [ 26 | CodeLine('abc'), 27 | CodeLine('foo'), 28 | CodeLine('bar') 29 | ])); 30 | }); 31 | 32 | test('toCodeLines', () async { 33 | expect(await CodeLineUtils.toCodeLinesAsync('abc\nfoo\nbar'), CodeLines.of(const [ 34 | CodeLine('abc'), 35 | CodeLine('foo'), 36 | CodeLine('bar') 37 | ])); 38 | }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /test/code_search_option_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:re_editor/re_editor.dart'; 3 | 4 | void main() { 5 | group('CodeFindOption getter ', () { 6 | test('`regExp`', () async { 7 | { 8 | const CodeFindOption option = CodeFindOption( 9 | pattern: 'a', 10 | caseSensitive: false, 11 | regex: false 12 | ); 13 | expect(option.regExp, RegExp('a', caseSensitive: false)); 14 | } 15 | { 16 | const CodeFindOption option = CodeFindOption( 17 | pattern: 'a', 18 | caseSensitive: true, 19 | regex: false 20 | ); 21 | expect(option.regExp, RegExp('a', caseSensitive: true)); 22 | } 23 | { 24 | const CodeFindOption option = CodeFindOption( 25 | pattern: 'a', 26 | caseSensitive: false, 27 | regex: true 28 | ); 29 | expect(option.regExp, RegExp('a', caseSensitive: false)); 30 | } 31 | { 32 | const CodeFindOption option = CodeFindOption( 33 | pattern: '*', 34 | caseSensitive: false, 35 | regex: false 36 | ); 37 | expect(option.regExp, RegExp('\\*', caseSensitive: false)); 38 | } 39 | }); 40 | }); 41 | group('CodeFindOption method ', () { 42 | test('`copyWith`', () async { 43 | const CodeFindOption option = CodeFindOption( 44 | pattern: '', 45 | caseSensitive: false, 46 | regex: false 47 | ); 48 | expect(option.copyWith(), option); 49 | expect(option.copyWith( 50 | pattern: 'a' 51 | ), const CodeFindOption( 52 | pattern: 'a', 53 | caseSensitive: false, 54 | regex: false 55 | )); 56 | expect(option.copyWith( 57 | caseSensitive: true, 58 | ), const CodeFindOption( 59 | pattern: '', 60 | caseSensitive: true, 61 | regex: false 62 | )); 63 | expect(option.copyWith( 64 | regex: true, 65 | ), const CodeFindOption( 66 | pattern: '', 67 | caseSensitive: false, 68 | regex: true 69 | )); 70 | expect(option.copyWith( 71 | pattern: 'abc', 72 | caseSensitive: true, 73 | regex: true 74 | ), const CodeFindOption( 75 | pattern: 'abc', 76 | caseSensitive: true, 77 | regex: true 78 | )); 79 | }); 80 | }); 81 | group('CodeFindOption operator ', () { 82 | test('`==`', () async { 83 | expect(const CodeFindOption( 84 | pattern: 'a', 85 | caseSensitive: true, 86 | regex: true 87 | ) == const CodeFindOption( 88 | pattern: 'a', 89 | caseSensitive: true, 90 | regex: true 91 | ), true); 92 | expect(const CodeFindOption( 93 | pattern: 'a', 94 | caseSensitive: true, 95 | regex: true 96 | ) == const CodeFindOption( 97 | pattern: 'b', 98 | caseSensitive: true, 99 | regex: true 100 | ), false); 101 | expect(const CodeFindOption( 102 | pattern: 'a', 103 | caseSensitive: false, 104 | regex: true 105 | ) == const CodeFindOption( 106 | pattern: 'a', 107 | caseSensitive: true, 108 | regex: true 109 | ), false); 110 | expect(const CodeFindOption( 111 | pattern: 'a', 112 | caseSensitive: true, 113 | regex: false 114 | ) == const CodeFindOption( 115 | pattern: 'a', 116 | caseSensitive: true, 117 | regex: true 118 | ), false); 119 | }); 120 | }); 121 | } -------------------------------------------------------------------------------- /test/code_search_value_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:re_editor/re_editor.dart'; 3 | 4 | void main() { 5 | group('CodeFindValue constructor ', () { 6 | test('`empty`', () async { 7 | const CodeFindValue value = CodeFindValue.empty(); 8 | expect(value.option, const CodeFindOption( 9 | pattern: '', 10 | caseSensitive: false, 11 | regex: false, 12 | )); 13 | expect(value.replaceMode, false); 14 | expect(value.result, null); 15 | }); 16 | }); 17 | group('CodeFindValue method ', () { 18 | test('`copyWith`', () async { 19 | const CodeFindValue value = CodeFindValue.empty(); 20 | { 21 | final CodeFindValue newValue = value.copyWith( 22 | result: CodeFindResult( 23 | index: 0, 24 | matches: [], 25 | option: value.option, 26 | codeLines: const CodeLines([]), 27 | dirty: false 28 | ) 29 | ); 30 | expect(newValue.result, CodeFindResult( 31 | index: 0, 32 | matches: [], 33 | option: value.option, 34 | codeLines: const CodeLines([]), 35 | dirty: false 36 | )); 37 | expect(newValue.replaceMode, value.replaceMode); 38 | expect(newValue.option, value.option); 39 | } 40 | { 41 | final CodeFindValue newValue = value.copyWith( 42 | replaceMode: true, 43 | result: null 44 | ); 45 | expect(newValue.result, null); 46 | expect(newValue.replaceMode, true); 47 | expect(newValue.option, value.option); 48 | } 49 | { 50 | final CodeFindValue newValue = value.copyWith( 51 | option: const CodeFindOption( 52 | pattern: 'a', 53 | caseSensitive: true, 54 | regex: true 55 | ), 56 | result: null 57 | ); 58 | expect(newValue.result, null); 59 | expect(newValue.replaceMode, value.replaceMode); 60 | expect(newValue.option, const CodeFindOption( 61 | pattern: 'a', 62 | caseSensitive: true, 63 | regex: true 64 | )); 65 | } 66 | 67 | }); 68 | }); 69 | group('CodeFindValue operator ', () { 70 | test('`==`', () async { 71 | expect(const CodeFindValue( 72 | option: CodeFindOption( 73 | pattern: '', 74 | caseSensitive: false, 75 | regex: false, 76 | ), 77 | replaceMode: false, 78 | result: CodeFindResult( 79 | index: 0, 80 | matches: [], 81 | option: CodeFindOption( 82 | pattern: '', 83 | caseSensitive: false, 84 | regex: false, 85 | ), 86 | codeLines: CodeLines([]), 87 | dirty: false 88 | ) 89 | ) == const CodeFindValue( 90 | option: CodeFindOption( 91 | pattern: '', 92 | caseSensitive: false, 93 | regex: false, 94 | ), 95 | replaceMode: false, 96 | result: CodeFindResult( 97 | index: 0, 98 | matches: [], 99 | option: CodeFindOption( 100 | pattern: '', 101 | caseSensitive: false, 102 | regex: false, 103 | ), 104 | codeLines: CodeLines([]), 105 | dirty: false 106 | ) 107 | ), true); 108 | expect(const CodeFindValue( 109 | option: CodeFindOption( 110 | pattern: '', 111 | caseSensitive: false, 112 | regex: false, 113 | ), 114 | replaceMode: false, 115 | result: CodeFindResult( 116 | index: 0, 117 | matches: [], 118 | option: CodeFindOption( 119 | pattern: '', 120 | caseSensitive: false, 121 | regex: false, 122 | ), 123 | codeLines: CodeLines([]), 124 | dirty: false 125 | ) 126 | ) == const CodeFindValue( 127 | option: CodeFindOption( 128 | pattern: 'a', 129 | caseSensitive: false, 130 | regex: false, 131 | ), 132 | replaceMode: false, 133 | result: CodeFindResult( 134 | index: 0, 135 | matches: [], 136 | option: CodeFindOption( 137 | pattern: '', 138 | caseSensitive: false, 139 | regex: false, 140 | ), 141 | codeLines: CodeLines([]), 142 | dirty: false 143 | ) 144 | ), false); 145 | expect(const CodeFindValue( 146 | option: CodeFindOption( 147 | pattern: '', 148 | caseSensitive: false, 149 | regex: false, 150 | ), 151 | replaceMode: false, 152 | result: CodeFindResult( 153 | index: 0, 154 | matches: [], 155 | option: CodeFindOption( 156 | pattern: '', 157 | caseSensitive: false, 158 | regex: false, 159 | ), 160 | codeLines: CodeLines([]), 161 | dirty: false 162 | ) 163 | ) == const CodeFindValue( 164 | option: CodeFindOption( 165 | pattern: '', 166 | caseSensitive: false, 167 | regex: false, 168 | ), 169 | replaceMode: true, 170 | result: CodeFindResult( 171 | index: 0, 172 | matches: [], 173 | option: CodeFindOption( 174 | pattern: '', 175 | caseSensitive: false, 176 | regex: false, 177 | ), 178 | codeLines: CodeLines([]), 179 | dirty: false 180 | ) 181 | ), false); 182 | expect(const CodeFindValue( 183 | option: CodeFindOption( 184 | pattern: '', 185 | caseSensitive: false, 186 | regex: false, 187 | ), 188 | replaceMode: false, 189 | result: CodeFindResult( 190 | index: 0, 191 | matches: [], 192 | option: CodeFindOption( 193 | pattern: '', 194 | caseSensitive: false, 195 | regex: false, 196 | ), 197 | codeLines: CodeLines([]), 198 | dirty: false 199 | ) 200 | ) == const CodeFindValue( 201 | option: CodeFindOption( 202 | pattern: '', 203 | caseSensitive: false, 204 | regex: false, 205 | ), 206 | replaceMode: false, 207 | result: CodeFindResult( 208 | index: 1, 209 | matches: [], 210 | option: CodeFindOption( 211 | pattern: '', 212 | caseSensitive: false, 213 | regex: false, 214 | ), 215 | codeLines: CodeLines([]), 216 | dirty: false 217 | ) 218 | ), false); 219 | }); 220 | }); 221 | } -------------------------------------------------------------------------------- /test/data/json_flatted.json: -------------------------------------------------------------------------------- 1 | {"abc": {"foo": ["bar"]},"123": [{"foo": "bar","bar": "foo"},{"foo": "bar","bar": "foo"}],"foo": "bar"} -------------------------------------------------------------------------------- /test/data/json_pretty.json: -------------------------------------------------------------------------------- 1 | { 2 | "abc": { 3 | "foo": [ 4 | "bar" 5 | ] 6 | }, 7 | "123": [ 8 | { 9 | "foo": "bar", 10 | "bar": "foo" 11 | }, 12 | { 13 | "foo": "bar", 14 | "bar": "foo" 15 | } 16 | ], 17 | "foo": "bar" 18 | } --------------------------------------------------------------------------------