├── res └── values │ └── strings_en.arb ├── lib └── editor │ └── src │ └── extensions.dart ├── rich_editor ├── LICENSE ├── CHANGELOG.md ├── .gitignore ├── res │ ├── images │ │ ├── format_overline.png │ │ ├── 1.5x │ │ │ └── format_overline.png │ │ └── 2.0x │ │ │ └── format_overline.png │ └── fonts │ │ ├── Lobster │ │ ├── Lobster-Regular.ttf │ │ └── OFL.txt │ │ ├── Satisfy │ │ ├── Satisfy-Regular.ttf │ │ └── LICENSE.txt │ │ ├── Comfortaa │ │ ├── Comfortaa-Bold.ttf │ │ ├── Comfortaa-Light.ttf │ │ ├── Comfortaa-Regular.ttf │ │ └── OFL.txt │ │ ├── Indie_Flower │ │ ├── IndieFlower.ttf │ │ └── OFL.txt │ │ ├── Tangerine │ │ ├── Tangerine-Bold.ttf │ │ ├── Tangerine-Regular.ttf │ │ └── OFL.txt │ │ ├── Berkshire_Swash │ │ ├── BerkshireSwash-Regular.ttf │ │ └── OFL.txt │ │ └── Cinzel_Decorative │ │ ├── CinzelDecorative-Black.ttf │ │ ├── CinzelDecorative-Bold.ttf │ │ ├── CinzelDecorative-Regular.ttf │ │ └── OFL.txt ├── lib │ ├── rich_editor.dart │ └── src │ │ ├── material │ │ ├── text_selection.dart │ │ └── rich_text_field.dart │ │ ├── extensions.dart │ │ ├── cupertino │ │ └── text_selection.dart │ │ ├── services │ │ └── text_input.dart │ │ └── widgets │ │ └── format_toolbar.dart ├── README.md ├── rich_editor.iml ├── pubspec.yaml └── test │ └── extensions_test.dart ├── rich_editor_demo ├── android │ ├── gradle.properties │ ├── images │ │ ├── screen_01.png │ │ ├── screen_01t.png │ │ └── screen_02.png │ ├── key.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── app │ │ ├── src │ │ │ └── main │ │ │ │ ├── res │ │ │ │ ├── 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 │ │ │ │ │ └── styles.xml │ │ │ │ └── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── java │ │ │ │ └── eu │ │ │ │ │ └── long1 │ │ │ │ │ └── richeditordemo │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── .gitignore │ ├── gen │ │ └── eu │ │ │ └── long1 │ │ │ └── richeditordemo │ │ │ ├── R.java │ │ │ ├── Manifest.java │ │ │ └── BuildConfig.java │ ├── settings.gradle │ ├── build.gradle │ ├── gradlew.bat │ └── gradlew ├── ios │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── AppDelegate.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── 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 │ │ │ │ └── Contents.json │ │ ├── main.m │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ └── project.pbxproj │ └── .gitignore ├── .gitignore ├── README.md ├── pubspec.yaml ├── rich_editor_demo.iml ├── android.iml ├── test │ └── widget_test.dart ├── rich_editor_demo_android.iml └── lib │ └── main.dart ├── .gitignore ├── README.md └── LICENSE /res/values/strings_en.arb: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /lib/editor/src/extensions.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rich_editor/LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /rich_editor_demo/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /rich_editor_demo/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /rich_editor_demo/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /rich_editor/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.0.1] - TODO: Add release date. 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /rich_editor/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea 4 | .packages 5 | .pub/ 6 | packages 7 | pubspec.lock 8 | -------------------------------------------------------------------------------- /rich_editor/res/images/format_overline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/images/format_overline.png -------------------------------------------------------------------------------- /rich_editor_demo/android/images/screen_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/android/images/screen_01.png -------------------------------------------------------------------------------- /rich_editor_demo/android/images/screen_01t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/android/images/screen_01t.png -------------------------------------------------------------------------------- /rich_editor_demo/android/images/screen_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/android/images/screen_02.png -------------------------------------------------------------------------------- /rich_editor/res/fonts/Lobster/Lobster-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Lobster/Lobster-Regular.ttf -------------------------------------------------------------------------------- /rich_editor/res/fonts/Satisfy/Satisfy-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Satisfy/Satisfy-Regular.ttf -------------------------------------------------------------------------------- /rich_editor/res/images/1.5x/format_overline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/images/1.5x/format_overline.png -------------------------------------------------------------------------------- /rich_editor/res/images/2.0x/format_overline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/images/2.0x/format_overline.png -------------------------------------------------------------------------------- /rich_editor/res/fonts/Comfortaa/Comfortaa-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Comfortaa/Comfortaa-Bold.ttf -------------------------------------------------------------------------------- /rich_editor/res/fonts/Comfortaa/Comfortaa-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Comfortaa/Comfortaa-Light.ttf -------------------------------------------------------------------------------- /rich_editor/res/fonts/Indie_Flower/IndieFlower.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Indie_Flower/IndieFlower.ttf -------------------------------------------------------------------------------- /rich_editor/res/fonts/Tangerine/Tangerine-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Tangerine/Tangerine-Bold.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea 4 | .packages 5 | .pub/ 6 | build/ 7 | ios/.generated/ 8 | packages 9 | pubspec.lock 10 | .flutter-plugins 11 | -------------------------------------------------------------------------------- /rich_editor/res/fonts/Comfortaa/Comfortaa-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Comfortaa/Comfortaa-Regular.ttf -------------------------------------------------------------------------------- /rich_editor/res/fonts/Tangerine/Tangerine-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Tangerine/Tangerine-Regular.ttf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rich_editor 2 | This is an RichEditor for the Flutter platform. 3 | Demo App: 4 | https://play.google.com/store/apps/details?id=eu.long1.richeditordemo 5 | -------------------------------------------------------------------------------- /rich_editor_demo/android/key.properties: -------------------------------------------------------------------------------- 1 | storePassword=sfBfi@44 2 | keyPassword=sfBfi@44 3 | keyAlias=richeditor demo 4 | storeFile=/home/long1eu/Desktop/keys/all.jks -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /rich_editor_demo/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /rich_editor/res/fonts/Berkshire_Swash/BerkshireSwash-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Berkshire_Swash/BerkshireSwash-Regular.ttf -------------------------------------------------------------------------------- /rich_editor_demo/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea 4 | .packages 5 | .pub/ 6 | build/ 7 | ios/.generated/ 8 | packages 9 | pubspec.lock 10 | .flutter-plugins 11 | -------------------------------------------------------------------------------- /rich_editor/res/fonts/Cinzel_Decorative/CinzelDecorative-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Cinzel_Decorative/CinzelDecorative-Black.ttf -------------------------------------------------------------------------------- /rich_editor/res/fonts/Cinzel_Decorative/CinzelDecorative-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Cinzel_Decorative/CinzelDecorative-Bold.ttf -------------------------------------------------------------------------------- /rich_editor/res/fonts/Cinzel_Decorative/CinzelDecorative-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor/res/fonts/Cinzel_Decorative/CinzelDecorative-Regular.ttf -------------------------------------------------------------------------------- /rich_editor_demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /rich_editor_demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /rich_editor_demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /rich_editor_demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /rich_editor_demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /rich_editor_demo/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | GeneratedPluginRegistrant.java 10 | -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /rich_editor_demo/README.md: -------------------------------------------------------------------------------- 1 | # rich_editor_demo 2 | 3 | Demo app for the rich editor. 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online 8 | [documentation](http://flutter.io/). 9 | -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/long1eu/rich_editor/HEAD/rich_editor_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /rich_editor/lib/rich_editor.dart: -------------------------------------------------------------------------------- 1 | library rich_editor; 2 | 3 | export 'package:rich_editor/src/material/rich_text_field.dart'; 4 | export 'package:rich_editor/src/widgets/rich_editable_text.dart'; 5 | export 'package:rich_editor/src/widgets/format_toolbar.dart'; -------------------------------------------------------------------------------- /rich_editor_demo/android/gen/eu/long1/richeditordemo/R.java: -------------------------------------------------------------------------------- 1 | /*___Generated_by_IDEA___*/ 2 | 3 | package eu.long1.richeditordemo; 4 | 5 | /* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */ 6 | public final class R { 7 | } -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /rich_editor_demo/android/gen/eu/long1/richeditordemo/Manifest.java: -------------------------------------------------------------------------------- 1 | /*___Generated_by_IDEA___*/ 2 | 3 | package eu.long1.richeditordemo; 4 | 5 | /* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */ 6 | public final class Manifest { 7 | } -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /rich_editor_demo/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /rich_editor_demo/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Nov 10 13:04:09 EET 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-all.zip 7 | -------------------------------------------------------------------------------- /rich_editor_demo/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: rich_editor_demo 2 | description: Demo app for the rich editor. 3 | 4 | dependencies: 5 | flutter: 6 | sdk: flutter 7 | 8 | rich_editor: 9 | path: ../rich_editor 10 | 11 | cupertino_icons: ^0.1.0 12 | 13 | 14 | flutter: 15 | 16 | uses-material-design: true -------------------------------------------------------------------------------- /rich_editor/README.md: -------------------------------------------------------------------------------- 1 | # rich_editor 2 | 3 | Rich editor for flutter platform 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online [documentation](http://flutter.io/). 8 | 9 | For help on editing package code, view the [documentation](https://flutter.io/developing-packages/). 10 | -------------------------------------------------------------------------------- /rich_editor_demo/android/gen/eu/long1/richeditordemo/BuildConfig.java: -------------------------------------------------------------------------------- 1 | /*___Generated_by_IDEA___*/ 2 | 3 | package eu.long1.richeditordemo; 4 | 5 | /* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */ 6 | public final class BuildConfig { 7 | public final static boolean DEBUG = Boolean.parseBoolean(null); 8 | } -------------------------------------------------------------------------------- /rich_editor_demo/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. -------------------------------------------------------------------------------- /rich_editor_demo/android/app/src/main/java/eu/long1/richeditordemo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package eu.long1.richeditordemo 2 | 3 | import android.os.Bundle 4 | 5 | import io.flutter.app.FlutterActivity 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity : FlutterActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | GeneratedPluginRegistrant.registerWith(this) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /rich_editor_demo/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 7 | [GeneratedPluginRegistrant registerWithRegistry:self]; 8 | // Override point for customization after application launch. 9 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 10 | } 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /rich_editor_demo/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 | -------------------------------------------------------------------------------- /rich_editor_demo/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withInputStream { stream -> plugins.load(stream) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /rich_editor_demo/rich_editor_demo.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /rich_editor_demo/android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /rich_editor_demo/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | *.pbxuser 16 | *.mode1v3 17 | *.mode2v3 18 | *.perspectivev3 19 | 20 | !default.pbxuser 21 | !default.mode1v3 22 | !default.mode2v3 23 | !default.perspectivev3 24 | 25 | xcuserdata 26 | 27 | *.moved-aside 28 | 29 | *.pyc 30 | *sync/ 31 | Icon? 32 | .tags* 33 | 34 | /Flutter/app.flx 35 | /Flutter/app.zip 36 | /Flutter/App.framework 37 | /Flutter/Flutter.framework 38 | /Flutter/Generated.xcconfig 39 | /ServiceDefinitions.json 40 | 41 | Pods/ 42 | -------------------------------------------------------------------------------- /rich_editor/rich_editor.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /rich_editor_demo/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.1.51' 3 | repositories { 4 | jcenter() 5 | maven { 6 | url "https://maven.google.com" 7 | } 8 | } 9 | 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:2.3.2' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | maven { 20 | url "https://maven.google.com" 21 | } 22 | } 23 | } 24 | 25 | rootProject.buildDir = '../build' 26 | subprojects { 27 | project.buildDir = "${rootProject.buildDir}/${project.name}" 28 | project.evaluationDependsOn(':app') 29 | } 30 | 31 | task clean(type: Delete) { 32 | delete rootProject.buildDir 33 | } 34 | -------------------------------------------------------------------------------- /rich_editor_demo/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 | UIRequiredDeviceCapabilities 24 | 25 | arm64 26 | 27 | MinimumOSVersion 28 | 8.0 29 | 30 | 31 | -------------------------------------------------------------------------------- /rich_editor_demo/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import '../lib/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new MyApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /rich_editor_demo/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 10 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /rich_editor_demo/rich_editor_demo_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /rich_editor/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: rich_editor 2 | description: Rich editor for flutter platform 3 | version: 0.0.1 4 | author: 5 | homepage: 6 | 7 | dependencies: 8 | flutter: 9 | sdk: flutter 10 | flutter_logger: ^0.0.1 11 | material_color_picker: ^0.0.1 12 | 13 | dev_dependencies: 14 | flutter_test: 15 | sdk: flutter 16 | 17 | flutter: 18 | 19 | uses-material-design: true 20 | 21 | assets: 22 | - res/images/format_overline.png 23 | 24 | fonts: 25 | - family: Berkshire Swash 26 | fonts: 27 | - asset: res/fonts/Berkshire_Swash/BerkshireSwash-Regular.ttf 28 | - family: Cinzel Decorative 29 | fonts: 30 | - asset: res/fonts/Cinzel_Decorative/CinzelDecorative-Regular.ttf 31 | - asset: res/fonts/Cinzel_Decorative/CinzelDecorative-Bold.ttf 32 | weight: 700 33 | - asset: res/fonts/Cinzel_Decorative/CinzelDecorative-Black.ttf 34 | weight: 900 35 | - family: Comfortaa 36 | fonts: 37 | - asset: res/fonts/Comfortaa/Comfortaa-Regular.ttf 38 | - asset: res/fonts/Comfortaa/Comfortaa-Light.ttf 39 | weight: 300 40 | - asset: res/fonts/Comfortaa/Comfortaa-Bold.ttf 41 | weight: 700 42 | - family: Indie Flower 43 | fonts: 44 | - asset: res/fonts/Indie_Flower/IndieFlower.ttf 45 | - family: Lobster 46 | fonts: 47 | - asset: res/fonts/Lobster/Lobster-Regular.ttf 48 | - family: Satisfy 49 | fonts: 50 | - asset: res/fonts/Satisfy/Satisfy-Regular.ttf 51 | - family: Tangerine 52 | fonts: 53 | - asset: res/fonts/Tangerine/Tangerine-Regular.ttf 54 | - asset: res/fonts/Tangerine/Tangerine-Bold.ttf 55 | weight: 700 -------------------------------------------------------------------------------- /rich_editor_demo/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 | -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | rich_editor_demo 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | arm64 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /rich_editor_demo/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withInputStream { stream -> 5 | localProperties.load(stream) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | apply plugin: 'com.android.application' 15 | apply plugin: 'kotlin-android' 16 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 17 | 18 | android { 19 | compileSdkVersion 26 20 | buildToolsVersion '26.0.3' 21 | 22 | lintOptions { 23 | disable 'InvalidPackage' 24 | } 25 | 26 | def keystorePropertiesFile = rootProject.file("key.properties") 27 | def keystoreProperties = new Properties() 28 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 29 | 30 | defaultConfig { 31 | applicationId "eu.long1.richeditordemo" 32 | minSdkVersion 16 33 | targetSdkVersion 27 34 | versionCode 2 35 | versionName "0.0.2" 36 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 37 | } 38 | 39 | signingConfigs { 40 | release { 41 | keyAlias keystoreProperties['keyAlias'] 42 | keyPassword keystoreProperties['keyPassword'] 43 | storeFile file(keystoreProperties['storeFile']) 44 | storePassword keystoreProperties['storePassword'] 45 | } 46 | } 47 | 48 | buildTypes { 49 | release { 50 | signingConfig signingConfigs.release 51 | } 52 | } 53 | } 54 | 55 | flutter { 56 | source '../..' 57 | } 58 | 59 | dependencies { 60 | androidTestCompile 'com.android.support.test:runner:1.0.1' 61 | androidTestCompile 'com.android.support.test:rules:1.0.1' 62 | compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 63 | } 64 | repositories { 65 | mavenCentral() 66 | } 67 | -------------------------------------------------------------------------------- /rich_editor_demo/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:rich_editor/rich_editor.dart'; 3 | 4 | void main() { 5 | runApp(new MyApp()); 6 | } 7 | 8 | class MyApp extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return new MaterialApp( 12 | title: 'Flutter Demo', 13 | theme: new ThemeData( 14 | primarySwatch: Colors.blue, 15 | ), 16 | home: new MyHomePage(title: 'RichField Demo'), 17 | ); 18 | } 19 | } 20 | 21 | class MyHomePage extends StatefulWidget { 22 | MyHomePage({Key key, this.title}) : super(key: key); 23 | 24 | final String title; 25 | 26 | @override 27 | _MyHomePageState createState() => new _MyHomePageState(); 28 | } 29 | 30 | class _MyHomePageState extends State { 31 | GlobalKey _richTextFieldState = 32 | new GlobalKey(); 33 | StyleController _styleController; 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | TextStyle baseStyle = Theme.of(context).textTheme.subhead; 38 | _styleController = new StyleController(style: baseStyle); 39 | 40 | return new Scaffold( 41 | appBar: new AppBar( 42 | title: new Text(widget.title), 43 | ), 44 | body: new Container( 45 | child: new Column( 46 | mainAxisAlignment: MainAxisAlignment.start, 47 | children: [ 48 | new Expanded( 49 | child: new Container( 50 | padding: new EdgeInsets.all(16.0), 51 | child: new Container( 52 | decoration: new BoxDecoration( 53 | border: new Border.all( 54 | color: Theme.of(context).primaryColor)), 55 | child: new RichTextField( 56 | key: _richTextFieldState, 57 | styleController: _styleController, 58 | maxLines: null, 59 | decoration: null, 60 | style: baseStyle, 61 | ), 62 | ), 63 | ), 64 | ), 65 | new FormatToolbar( 66 | styleController: _styleController, 67 | richTextFieldState: _richTextFieldState, 68 | ) 69 | ], 70 | ), 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /rich_editor_demo/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 | -------------------------------------------------------------------------------- /rich_editor_demo/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /rich_editor_demo/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 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /rich_editor/res/fonts/Tangerine/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Toshi Omagari (tosche@mac.com) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /rich_editor/res/fonts/Indie_Flower/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Kimberly Geswein (kimberlygeswein.com) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /rich_editor/res/fonts/Cinzel_Decorative/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Natanael Gama (info@ndiscovered.com), with Reserved Font Name 'Cinzel' 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /rich_editor/res/fonts/Comfortaa/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2011 The Comfortaa Project Authors (https://github.com/alexeiva/comfortaa), with Reserved Font Name "Comfortaa". 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /rich_editor/res/fonts/Lobster/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010 The Lobster Project Authors (https://github.com/impallari/The-Lobster-Font), with Reserved Font Name "Lobster". 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /rich_editor/res/fonts/Berkshire_Swash/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 by Brian J. Bonislawsky DBA Astigmatic (AOETI) (astigma@astigmatic.com), with Reserved Font Names "Berkshire Swash" 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /rich_editor_demo/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /rich_editor/test/extensions_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:rich_editor/src/extensions.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | void main() { 6 | test('isEmpty test', () => isEmptyTest()); 7 | test('length test', () => lengthTest()); 8 | test('getOffsetInParent test', () => getOffsetInParentTest()); 9 | test('maxFontSize test', () => maxFontSizeTest()); 10 | test('copySpanWith test', () => copySpanWithTest()); 11 | test('copyStyleWith test', () => copyStyleWithTest()); 12 | test('deepMerge test', () => deepMergeTest()); 13 | test('getDifferenceStyle test', () => getDifferenceStyleTest()); 14 | } 15 | 16 | void isEmptyTest() { 17 | expect( 18 | Extensions.isEmpty( 19 | new TextSpan(text: null), 20 | ), 21 | true); 22 | expect( 23 | Extensions.isEmpty( 24 | new TextSpan(text: ""), 25 | ), 26 | true); 27 | expect( 28 | Extensions.isEmpty( 29 | new TextSpan( 30 | text: "ss", 31 | ), 32 | ), 33 | false); 34 | expect( 35 | Extensions.isEmpty( 36 | new TextSpan( 37 | text: "", 38 | children: [new TextSpan(text: "")], 39 | ), 40 | ), 41 | true); 42 | expect( 43 | Extensions.isEmpty( 44 | new TextSpan( 45 | text: "a", 46 | children: [new TextSpan(text: "")], 47 | ), 48 | ), 49 | false); 50 | expect( 51 | Extensions.isEmpty( 52 | new TextSpan( 53 | text: "", 54 | children: [new TextSpan(text: "a")], 55 | ), 56 | ), 57 | false); 58 | } 59 | 60 | void lengthTest() { 61 | expect( 62 | Extensions.length( 63 | new TextSpan(text: null), 64 | ), 65 | 0); 66 | expect( 67 | Extensions.length( 68 | new TextSpan(text: ""), 69 | ), 70 | 0); 71 | expect( 72 | Extensions.length( 73 | new TextSpan( 74 | text: "ss", 75 | ), 76 | ), 77 | 2); 78 | expect( 79 | Extensions.length( 80 | new TextSpan( 81 | text: "", 82 | children: [new TextSpan(text: "")], 83 | ), 84 | ), 85 | 0); 86 | expect( 87 | Extensions.length( 88 | new TextSpan( 89 | text: "ae", 90 | children: [new TextSpan(text: "")], 91 | ), 92 | ), 93 | 2); 94 | expect( 95 | Extensions.length( 96 | new TextSpan( 97 | text: "", 98 | children: [ 99 | new TextSpan(text: "a"), 100 | new TextSpan(text: "as"), 101 | new TextSpan(text: "aa") 102 | ], 103 | ), 104 | ), 105 | 5); 106 | } 107 | 108 | void getOffsetInParentTest() { 109 | // 110 | expect(Extensions.getOffsetInParent(new TextSpan(text: ""), null), -1); 111 | 112 | // 113 | expect( 114 | Extensions.getOffsetInParent( 115 | new TextSpan(text: ""), 116 | new TextSpan(text: ""), 117 | ), 118 | -1); 119 | 120 | // 121 | TextSpan child = new TextSpan(text: ""); 122 | TextSpan parent = new TextSpan(text: "", children: [child]); 123 | 124 | expect(Extensions.getOffsetInParent(parent, child), 0); 125 | 126 | // 127 | child = new TextSpan(text: "a"); 128 | parent = new TextSpan(text: "ssasa", children: [child]); 129 | 130 | expect(Extensions.getOffsetInParent(parent, child), 5); 131 | 132 | // 133 | child = new TextSpan(text: "a"); 134 | parent = new TextSpan(text: "ssasa", children: [ 135 | new TextSpan(text: "ar t"), 136 | child, 137 | ]); 138 | 139 | expect(Extensions.getOffsetInParent(parent, child), 9); 140 | 141 | // 142 | child = new TextSpan(text: "a"); 143 | parent = new TextSpan(text: "ssasa", children: [ 144 | new TextSpan(text: "ar t"), 145 | child, 146 | new TextSpan(text: "ar t"), 147 | ]); 148 | 149 | expect(Extensions.getOffsetInParent(parent, child), 9); 150 | } 151 | 152 | maxFontSizeTest() { 153 | TextSpan root = new TextSpan( 154 | text: "", 155 | style: new TextStyle(fontSize: 15.0), 156 | children: [ 157 | new TextSpan(text: "", style: new TextStyle(fontSize: 16.0)), 158 | new TextSpan(text: "", style: new TextStyle(fontSize: 18.0)), 159 | new TextSpan(text: "", style: new TextStyle(fontSize: 13.0)), 160 | new TextSpan(text: "", style: new TextStyle(fontSize: 23.0)), 161 | ], 162 | ); 163 | 164 | expect(Extensions.maxFontSize(root), 23.0); 165 | } 166 | 167 | copySpanWithTest() { 168 | TextSpan root = new TextSpan( 169 | text: "", 170 | style: new TextStyle(fontSize: 15.0), 171 | children: [new TextSpan(text: " children ")], 172 | ); 173 | 174 | var test = "test"; 175 | 176 | TextSpan newSpan = Extensions.copySpanWith(base: root, text: test); 177 | 178 | expect( 179 | newSpan, 180 | new TextSpan( 181 | text: test, 182 | style: new TextStyle(fontSize: 15.0), 183 | children: [ 184 | new TextSpan(text: " children "), 185 | ], 186 | ), 187 | ); 188 | } 189 | 190 | copyStyleWithTest() { 191 | TextStyle root = new TextStyle(fontSize: 12.0, color: Colors.orange); 192 | 193 | TextStyle newStyle = Extensions.copyStyleWith(base: root, color: Colors.red); 194 | expect(newStyle.color, Colors.red); 195 | } 196 | 197 | deepMergeTest() { 198 | TextStyle root = new TextStyle( 199 | fontWeight: FontWeight.w700, 200 | decoration: TextDecoration.overline, 201 | ); 202 | 203 | TextStyle newStyle = Extensions.deepMerge( 204 | root, 205 | new TextStyle( 206 | decoration: TextDecoration.lineThrough, 207 | )); 208 | 209 | expect( 210 | newStyle.decoration, 211 | new TextDecoration.combine( 212 | [ 213 | TextDecoration.overline, 214 | TextDecoration.lineThrough, 215 | ], 216 | ), 217 | ); 218 | 219 | newStyle = Extensions.deepMerge( 220 | root, 221 | new TextStyle( 222 | decoration: TextDecoration.none, 223 | )); 224 | 225 | expect( 226 | newStyle.decoration, 227 | new TextDecoration.combine( 228 | [ 229 | TextDecoration.none, 230 | ], 231 | ), 232 | ); 233 | } 234 | 235 | getDifferenceStyleTest() { 236 | TextStyle original = new TextStyle( 237 | fontWeight: FontWeight.w700, 238 | decoration: TextDecoration.overline, 239 | ); 240 | TextStyle copy = Extensions.copyStyleWith(base: original, fontSize: 12.0); 241 | TextStyle differenceStyle = Extensions.getDifferenceStyle(original, copy); 242 | 243 | expect(differenceStyle, new TextStyle(fontSize: 12.0)); 244 | } 245 | -------------------------------------------------------------------------------- /rich_editor/lib/src/material/text_selection.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'dart:math' as math; 6 | 7 | import 'package:flutter/material.dart' 8 | hide TextSelectionDelegate, TextSelectionControls, TextSelectionHandleType; 9 | import 'package:flutter/rendering.dart'; 10 | import 'package:flutter/widgets.dart' 11 | hide TextSelectionDelegate, TextSelectionControls, TextSelectionHandleType; 12 | import 'package:rich_editor/src/services/text_input.dart'; 13 | import 'package:rich_editor/src/widgets/text_selection.dart'; 14 | 15 | const double _kHandleSize = 22.0; 16 | // Minimal padding from all edges of the selection toolbar to all edges of the 17 | // viewport. 18 | const double _kToolbarScreenPadding = 8.0; 19 | 20 | /// Manages a copy/paste text selection toolbar. 21 | class _TextSelectionToolbar extends StatelessWidget { 22 | const _TextSelectionToolbar({ 23 | Key key, 24 | this.delegate, 25 | this.handleCut, 26 | this.handleCopy, 27 | this.handlePaste, 28 | this.handleSelectAll, 29 | }) 30 | : super(key: key); 31 | 32 | final TextSelectionDelegate delegate; 33 | 34 | AbstractTextEditingValue get value => delegate.textEditingValue; 35 | 36 | final VoidCallback handleCut; 37 | final VoidCallback handleCopy; 38 | final VoidCallback handlePaste; 39 | final VoidCallback handleSelectAll; 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | final List items = []; 44 | final MaterialLocalizations localizations = 45 | MaterialLocalizations.of(context); 46 | 47 | if (!value.selection.isCollapsed) { 48 | items.add(new FlatButton( 49 | child: new Text(localizations.cutButtonLabel), onPressed: handleCut)); 50 | items.add(new FlatButton( 51 | child: new Text(localizations.copyButtonLabel), 52 | onPressed: handleCopy)); 53 | } 54 | items.add(new FlatButton( 55 | child: new Text(localizations.pasteButtonLabel), 56 | // TODO(https://github.com/flutter/flutter/issues/11254): 57 | // This should probably be grayed-out if there is nothing to paste. 58 | onPressed: handlePaste, 59 | )); 60 | if (value.isNotEmpty) { 61 | if (value.selection.isCollapsed) 62 | items.add(new FlatButton( 63 | child: new Text(localizations.selectAllButtonLabel), 64 | onPressed: handleSelectAll)); 65 | } 66 | 67 | return new Material( 68 | elevation: 1.0, 69 | child: new Container( 70 | height: 44.0, 71 | child: new Row(mainAxisSize: MainAxisSize.min, children: items))); 72 | } 73 | } 74 | 75 | /// Centers the toolbar around the given position, ensuring that it remains on 76 | /// screen. 77 | class _TextSelectionToolbarLayout extends SingleChildLayoutDelegate { 78 | _TextSelectionToolbarLayout( 79 | this.screenSize, this.globalEditableRegion, this.position); 80 | 81 | /// The size of the screen at the time that the toolbar was last laid out. 82 | final Size screenSize; 83 | 84 | /// Size and position of the editing region at the time the toolbar was last 85 | /// laid out, in global coordinates. 86 | final Rect globalEditableRegion; 87 | 88 | /// Anchor position of the toolbar, relative to the top left of the 89 | /// [globalEditableRegion]. 90 | final Offset position; 91 | 92 | @override 93 | BoxConstraints getConstraintsForChild(BoxConstraints constraints) { 94 | return constraints.loosen(); 95 | } 96 | 97 | @override 98 | Offset getPositionForChild(Size size, Size childSize) { 99 | final Offset globalPosition = globalEditableRegion.topLeft + position; 100 | 101 | double x = globalPosition.dx - childSize.width / 2.0; 102 | double y = globalPosition.dy - childSize.height; 103 | 104 | if (x < _kToolbarScreenPadding) 105 | x = _kToolbarScreenPadding; 106 | else if (x + childSize.width > screenSize.width - _kToolbarScreenPadding) 107 | x = screenSize.width - childSize.width - _kToolbarScreenPadding; 108 | 109 | if (y < _kToolbarScreenPadding) 110 | y = _kToolbarScreenPadding; 111 | else if (y + childSize.height > screenSize.height - _kToolbarScreenPadding) 112 | y = screenSize.height - childSize.height - _kToolbarScreenPadding; 113 | 114 | return new Offset(x, y); 115 | } 116 | 117 | @override 118 | bool shouldRelayout(_TextSelectionToolbarLayout oldDelegate) { 119 | return position != oldDelegate.position; 120 | } 121 | } 122 | 123 | /// Draws a single text selection handle. The [type] determines where the handle 124 | /// points (e.g. the [left] handle points up and to the right). 125 | class _TextSelectionHandlePainter extends CustomPainter { 126 | _TextSelectionHandlePainter({this.color}); 127 | 128 | final Color color; 129 | 130 | @override 131 | void paint(Canvas canvas, Size size) { 132 | final Paint paint = new Paint()..color = color; 133 | final double radius = size.width / 2.0; 134 | canvas.drawCircle(new Offset(radius, radius), radius, paint); 135 | canvas.drawRect(new Rect.fromLTWH(0.0, 0.0, radius, radius), paint); 136 | } 137 | 138 | @override 139 | bool shouldRepaint(_TextSelectionHandlePainter oldPainter) { 140 | return color != oldPainter.color; 141 | } 142 | } 143 | 144 | class _MaterialTextSelectionControls extends TextSelectionControls { 145 | @override 146 | Size handleSize = const Size(_kHandleSize, _kHandleSize); 147 | 148 | /// Builder for material-style copy/paste text selection toolbar. 149 | @override 150 | Widget buildToolbar(BuildContext context, Rect globalEditableRegion, 151 | Offset position, TextSelectionDelegate delegate) { 152 | assert(debugCheckHasMediaQuery(context)); 153 | return new ConstrainedBox( 154 | constraints: new BoxConstraints.tight(globalEditableRegion.size), 155 | child: new CustomSingleChildLayout( 156 | delegate: new _TextSelectionToolbarLayout( 157 | MediaQuery.of(context).size, 158 | globalEditableRegion, 159 | position, 160 | ), 161 | child: new _TextSelectionToolbar( 162 | delegate: delegate, 163 | handleCut: () => handleCut(delegate), 164 | handleCopy: () => handleCopy(delegate), 165 | handlePaste: () => handlePaste(delegate), 166 | handleSelectAll: () => handleSelectAll(delegate), 167 | ), 168 | )); 169 | } 170 | 171 | /// Builder for material-style text selection handles. 172 | @override 173 | Widget buildHandle( 174 | BuildContext context, TextSelectionHandleType type, double textHeight) { 175 | final Widget handle = new SizedBox( 176 | width: _kHandleSize, 177 | height: _kHandleSize, 178 | child: new CustomPaint( 179 | painter: new _TextSelectionHandlePainter( 180 | color: Theme.of(context).textSelectionHandleColor))); 181 | 182 | // [handle] is a circle, with a rectangle in the top left quadrant of that 183 | // circle (an onion pointing to 10:30). We rotate [handle] to point 184 | // straight up or up-right depending on the handle type. 185 | switch (type) { 186 | case TextSelectionHandleType.left: // points up-right 187 | return new Transform( 188 | transform: new Matrix4.rotationZ(math.PI / 2.0), child: handle); 189 | case TextSelectionHandleType.right: // points up-left 190 | return handle; 191 | case TextSelectionHandleType.collapsed: // points up 192 | return new Transform( 193 | transform: new Matrix4.rotationZ(math.PI / 4.0), child: handle); 194 | } 195 | assert(type != null); 196 | return null; 197 | } 198 | } 199 | 200 | /// Text selection controls that follow the Material Design specification. 201 | final TextSelectionControls materialTextSelectionControls = 202 | new _MaterialTextSelectionControls(); 203 | -------------------------------------------------------------------------------- /rich_editor/lib/src/extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/gestures.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class Extensions { 6 | static bool isEmpty(TextSpan textSpan) { 7 | assert(textSpan.debugAssertIsValid()); 8 | bool isEmpty = true; 9 | textSpan.visitTextSpan((TextSpan span) { 10 | if (span.text.isNotEmpty) { 11 | isEmpty = false; 12 | return false; 13 | } else 14 | return true; 15 | }); 16 | return isEmpty; 17 | } 18 | 19 | static bool isNotEmpty(TextSpan textSpan) => !isEmpty(textSpan); 20 | 21 | /// Return the length of the text contained in this [TextSpan] tree. 22 | static int length(TextSpan textSpan) { 23 | assert(textSpan.debugAssertIsValid()); 24 | int length = 0; 25 | textSpan.visitTextSpan((TextSpan span) { 26 | length += span.text.length; 27 | return true; 28 | }); 29 | return length; 30 | } 31 | 32 | /// Returns the text span that contains the given position in the text. 33 | static TextSpan getSpanForPosition(TextSpan parent, int targetOffset) { 34 | assert(parent.debugAssertIsValid()); 35 | int offset = 0; 36 | TextSpan result; 37 | parent.visitTextSpan((TextSpan span) { 38 | assert(result == null); 39 | final int endOffset = offset + span.text.length; 40 | if (targetOffset >= offset && targetOffset <= endOffset) { 41 | result = span; 42 | return false; 43 | } 44 | offset = endOffset; 45 | return true; 46 | }); 47 | return result; 48 | } 49 | 50 | /// Return the offset of this child in plain text or -1 if the children list 51 | /// is null or this [TextSpan] is not contained in this children list. 52 | static int getOffsetInParent(TextSpan parent, TextSpan span) { 53 | assert(parent.debugAssertIsValid()); 54 | if (parent.children == null || !parent.children.contains(span)) return -1; 55 | 56 | int length = 0; 57 | parent.visitTextSpan((TextSpan sibling) { 58 | if (span != sibling) { 59 | length += sibling.text.length; 60 | return true; 61 | } else 62 | return false; 63 | }); 64 | return length; 65 | } 66 | 67 | /// Return the max fontSize from a given [TextSpan] tree. 68 | static double maxFontSize(TextSpan textSpan) { 69 | textSpan.debugAssertIsValid(); 70 | double size = 0.0; 71 | textSpan.visitTextSpan((TextSpan span) { 72 | var currentSize = span.style?.fontSize ?? -1.0; 73 | if (currentSize > size) size = currentSize; 74 | return true; 75 | }); 76 | return size; 77 | } 78 | 79 | /// Creates a copy of this [TextSpan] but with the given fields replaced with the new values. 80 | static TextSpan copySpanWith( 81 | {@required TextSpan base, 82 | TextStyle style, 83 | String text, 84 | List children, 85 | GestureRecognizer recognizer}) { 86 | return new TextSpan( 87 | style: style ?? base.style, 88 | text: text ?? base.text, 89 | children: children ?? base.children, 90 | recognizer: recognizer ?? base.recognizer); 91 | } 92 | 93 | static TextStyle copyStyleWith({ 94 | @required TextStyle base, 95 | color, 96 | String fontFamily, 97 | fontSize, 98 | fontWeight, 99 | fontStyle, 100 | letterSpacing, 101 | wordSpacing, 102 | textBaseline, 103 | height, 104 | decoration, 105 | decorationColor, 106 | decorationStyle, 107 | package, 108 | }) { 109 | return new TextStyle( 110 | color: color ?? base.color, 111 | fontFamily: fontFamily ?? base.fontFamily, 112 | fontSize: fontSize ?? base.fontSize, 113 | fontWeight: fontWeight ?? base.fontWeight, 114 | fontStyle: fontStyle ?? base.fontStyle, 115 | letterSpacing: letterSpacing ?? base.letterSpacing, 116 | wordSpacing: wordSpacing ?? base.wordSpacing, 117 | textBaseline: textBaseline ?? base.textBaseline, 118 | height: height ?? base.height, 119 | decoration: decoration ?? base.decoration, 120 | decorationColor: decorationColor ?? base.decorationColor, 121 | decorationStyle: decorationStyle ?? base.decorationStyle, 122 | package: package, 123 | ); 124 | } 125 | 126 | /// Returns a new text style that matches this text style but with some values 127 | /// replaced by the non-null parameters of the given text style. This merge 128 | /// take into consideration the [TextDecoration] values and combines them. If 129 | /// the given text style is null, simply returns this text style. 130 | /// 131 | /// When [other] decoration is [TextDecoration.none], it overrides the base 132 | /// decoration fully. 133 | static TextStyle deepMerge(TextStyle base, TextStyle other) { 134 | if (other == null) return base; 135 | assert(other.inherit); 136 | 137 | TextDecoration decoration; 138 | 139 | if (other.decoration == null) 140 | decoration = base.decoration; 141 | else if (base.decoration == null || other.decoration == TextDecoration.none) 142 | decoration = other.decoration; 143 | else { 144 | if (_getDecorationList(base.decoration).length > 145 | _getDecorationList(other.decoration).length) { 146 | decoration = other.decoration; 147 | } else { 148 | decoration = new TextDecoration.combine( 149 | [base.decoration, other.decoration]); 150 | } 151 | } 152 | 153 | return new TextStyle( 154 | color: other.color ?? base.color, 155 | fontFamily: other.fontFamily ?? base.fontFamily, 156 | fontSize: other.fontSize ?? base.fontSize, 157 | fontWeight: other.fontWeight ?? base.fontWeight, 158 | fontStyle: other.fontStyle ?? base.fontStyle, 159 | letterSpacing: other.letterSpacing ?? base.letterSpacing, 160 | wordSpacing: other.wordSpacing ?? base.wordSpacing, 161 | textBaseline: other.textBaseline ?? base.textBaseline, 162 | height: other.height ?? base.height, 163 | decoration: decoration, 164 | decorationColor: other.decorationColor ?? base.decorationColor, 165 | decorationStyle: other.decorationStyle ?? base.decorationStyle, 166 | ); 167 | } 168 | 169 | /// Return a List with all the decoration contained by the [TextDecoration]. 170 | static List _getDecorationList(TextDecoration decoration) { 171 | final List decorationList = []; 172 | 173 | if (decoration == null) {} else if (decoration == TextDecoration.none) { 174 | decorationList.add(TextDecoration.none); 175 | } else { 176 | if (decoration.contains(TextDecoration.underline)) 177 | decorationList.add(TextDecoration.underline); 178 | if (decoration.contains(TextDecoration.overline)) 179 | decorationList.add(TextDecoration.overline); 180 | if (decoration.contains(TextDecoration.lineThrough)) 181 | decorationList.add(TextDecoration.lineThrough); 182 | } 183 | 184 | return decorationList; 185 | } 186 | 187 | /// Return a new TextStyle with the differences between the base style and the 188 | /// provided style. 189 | static TextStyle getDifferenceStyle(TextStyle base, TextStyle style) { 190 | return new TextStyle( 191 | color: base.color != style.color ? style.color : null, 192 | fontFamily: 193 | base.fontFamily != style.fontFamily ? style.fontFamily : null, 194 | fontSize: base.fontSize != style.fontSize ? style.fontSize : null, 195 | fontWeight: 196 | base.fontWeight != style.fontWeight ? style.fontWeight : null, 197 | fontStyle: base.fontStyle != style.fontStyle ? style.fontStyle : null, 198 | letterSpacing: base.letterSpacing != style.letterSpacing 199 | ? style.letterSpacing 200 | : null, 201 | wordSpacing: 202 | base.wordSpacing != style.wordSpacing ? style.wordSpacing : null, 203 | textBaseline: 204 | base.textBaseline != style.textBaseline ? style.textBaseline : null, 205 | height: base.height != style.height ? style.height : null, 206 | decoration: 207 | base.decoration != style.decoration ? style.decoration : null, 208 | decorationColor: base.decorationColor != style.decorationColor 209 | ? style.decorationColor 210 | : null, 211 | decorationStyle: base.decorationStyle != style.decorationStyle 212 | ? style.decorationStyle 213 | : null); 214 | } 215 | 216 | static TextStyle emptyStyle = const TextStyle(); 217 | } 218 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /rich_editor/res/fonts/Satisfy/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /rich_editor/lib/src/cupertino/text_selection.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'dart:math' as math; 6 | 7 | import 'package:flutter/cupertino.dart' 8 | hide TextSelectionControls, TextSelectionDelegate, TextSelectionHandleType; 9 | import 'package:flutter/rendering.dart'; 10 | import 'package:flutter/widgets.dart' 11 | hide TextSelectionControls, TextSelectionDelegate, TextSelectionHandleType; 12 | import 'package:rich_editor/src/services/text_input.dart'; 13 | import 'package:rich_editor/src/widgets/text_selection.dart'; 14 | 15 | // Padding around the line at the edge of the text selection that has 0 width and 16 | // the height of the text font. 17 | const double _kHandlesPadding = 18.0; 18 | // Minimal padding from all edges of the selection toolbar to all edges of the 19 | // viewport. 20 | const double _kToolbarScreenPadding = 8.0; 21 | const double _kToolbarHeight = 36.0; 22 | 23 | const Color _kToolbarBackgroundColor = const Color(0xFF2E2E2E); 24 | const Color _kToolbarDividerColor = const Color(0xFFB9B9B9); 25 | const Color _kHandlesColor = const Color(0xFF146DDE); 26 | 27 | // This offset is used to determine the center of the selection during a drag. 28 | // It's slightly below the center of the text so the finger isn't entirely 29 | // covering the text being selected. 30 | const Size _kSelectionOffset = const Size(20.0, 30.0); 31 | const Size _kToolbarTriangleSize = const Size(18.0, 9.0); 32 | const EdgeInsets _kToolbarButtonPadding = 33 | const EdgeInsets.symmetric(vertical: 10.0, horizontal: 21.0); 34 | const BorderRadius _kToolbarBorderRadius = 35 | const BorderRadius.all(const Radius.circular(7.5)); 36 | 37 | const TextStyle _kToolbarButtonFontStyle = const TextStyle( 38 | fontSize: 14.0, 39 | letterSpacing: -0.11, 40 | fontWeight: FontWeight.w300, 41 | ); 42 | 43 | /// Paints a triangle below the toolbar. 44 | class _TextSelectionToolbarNotchPainter extends CustomPainter { 45 | @override 46 | void paint(Canvas canvas, Size size) { 47 | final Paint paint = new Paint() 48 | ..color = _kToolbarBackgroundColor 49 | ..style = PaintingStyle.fill; 50 | final Path triangle = new Path() 51 | ..lineTo(_kToolbarTriangleSize.width / 2, 0.0) 52 | ..lineTo(0.0, _kToolbarTriangleSize.height) 53 | ..lineTo(-(_kToolbarTriangleSize.width / 2), 0.0) 54 | ..close(); 55 | canvas.drawPath(triangle, paint); 56 | } 57 | 58 | @override 59 | bool shouldRepaint(_TextSelectionToolbarNotchPainter oldPainter) => false; 60 | } 61 | 62 | /// Manages a copy/paste text selection toolbar. 63 | class _TextSelectionToolbar extends StatelessWidget { 64 | const _TextSelectionToolbar({ 65 | Key key, 66 | this.delegate, 67 | this.handleCut, 68 | this.handleCopy, 69 | this.handlePaste, 70 | this.handleSelectAll, 71 | }) 72 | : super(key: key); 73 | 74 | final TextSelectionDelegate delegate; 75 | 76 | AbstractTextEditingValue get editingValue => delegate.textEditingValue; 77 | 78 | final VoidCallback handleCut; 79 | final VoidCallback handleCopy; 80 | final VoidCallback handlePaste; 81 | final VoidCallback handleSelectAll; 82 | 83 | @override 84 | Widget build(BuildContext context) { 85 | final List items = []; 86 | final Widget onePhysicalPixelVerticalDivider = 87 | new SizedBox(width: 1.0 / MediaQuery.of(context).devicePixelRatio); 88 | 89 | if (!editingValue.selection.isCollapsed) { 90 | items.add(_buildToolbarButton('Cut', handleCut)); 91 | items.add(onePhysicalPixelVerticalDivider); 92 | items.add(_buildToolbarButton('Copy', handleCopy)); 93 | } 94 | 95 | // TODO(https://github.com/flutter/flutter/issues/11254): 96 | // This should probably be grayed-out if there is nothing to paste. 97 | if (items.isNotEmpty) items.add(onePhysicalPixelVerticalDivider); 98 | items.add(_buildToolbarButton('Paste', handlePaste)); 99 | 100 | if (editingValue.isNotEmpty && editingValue.selection.isCollapsed) { 101 | items.add(onePhysicalPixelVerticalDivider); 102 | items.add(_buildToolbarButton('Select All', handleSelectAll)); 103 | } 104 | 105 | final Widget triangle = new SizedBox.fromSize( 106 | size: _kToolbarTriangleSize, 107 | child: new CustomPaint( 108 | painter: new _TextSelectionToolbarNotchPainter(), 109 | )); 110 | 111 | return new Column( 112 | mainAxisSize: MainAxisSize.min, 113 | children: [ 114 | new ClipRRect( 115 | borderRadius: _kToolbarBorderRadius, 116 | child: new DecoratedBox( 117 | decoration: const BoxDecoration( 118 | color: _kToolbarDividerColor, 119 | ), 120 | child: new Row(mainAxisSize: MainAxisSize.min, children: items), 121 | ), 122 | ), 123 | // TODO(https://github.com/flutter/flutter/issues/11274): 124 | // Position the triangle based on the layout delegate. 125 | // And avoid letting the triangle line up with any dividers. 126 | triangle, 127 | ], 128 | ); 129 | } 130 | 131 | /// Builds a themed [CupertinoButton] for the toolbar. 132 | CupertinoButton _buildToolbarButton(String text, VoidCallback onPressed) { 133 | return new CupertinoButton( 134 | child: new Text(text, style: _kToolbarButtonFontStyle), 135 | color: _kToolbarBackgroundColor, 136 | minSize: _kToolbarHeight, 137 | padding: _kToolbarButtonPadding, 138 | borderRadius: null, 139 | pressedOpacity: 0.7, 140 | onPressed: onPressed, 141 | ); 142 | } 143 | } 144 | 145 | /// Centers the toolbar around the given position, ensuring that it remains on 146 | /// screen. 147 | class _TextSelectionToolbarLayout extends SingleChildLayoutDelegate { 148 | _TextSelectionToolbarLayout( 149 | this.screenSize, this.globalEditableRegion, this.position); 150 | 151 | /// The size of the screen at the time that the toolbar was last laid out. 152 | final Size screenSize; 153 | 154 | /// Size and position of the editing region at the time the toolbar was last 155 | /// laid out, in global coordinates. 156 | final Rect globalEditableRegion; 157 | 158 | /// Anchor position of the toolbar, relative to the top left of the 159 | /// [globalEditableRegion]. 160 | final Offset position; 161 | 162 | @override 163 | BoxConstraints getConstraintsForChild(BoxConstraints constraints) { 164 | return constraints.loosen(); 165 | } 166 | 167 | @override 168 | Offset getPositionForChild(Size size, Size childSize) { 169 | final Offset globalPosition = globalEditableRegion.topLeft + position; 170 | 171 | double x = globalPosition.dx - childSize.width / 2.0; 172 | double y = globalPosition.dy - childSize.height; 173 | 174 | if (x < _kToolbarScreenPadding) 175 | x = _kToolbarScreenPadding; 176 | else if (x + childSize.width > screenSize.width - _kToolbarScreenPadding) 177 | x = screenSize.width - childSize.width - _kToolbarScreenPadding; 178 | 179 | if (y < _kToolbarScreenPadding) 180 | y = _kToolbarScreenPadding; 181 | else if (y + childSize.height > screenSize.height - _kToolbarScreenPadding) 182 | y = screenSize.height - childSize.height - _kToolbarScreenPadding; 183 | 184 | return new Offset(x, y); 185 | } 186 | 187 | @override 188 | bool shouldRelayout(_TextSelectionToolbarLayout oldDelegate) { 189 | return screenSize != oldDelegate.screenSize || 190 | globalEditableRegion != oldDelegate.globalEditableRegion || 191 | position != oldDelegate.position; 192 | } 193 | } 194 | 195 | /// Draws a single text selection handle with a bar and a ball. 196 | /// 197 | /// Draws from a point of origin somewhere inside the size of the painter 198 | /// such that the ball is below the point of origin and the bar is above the 199 | /// point of origin. 200 | class _TextSelectionHandlePainter extends CustomPainter { 201 | _TextSelectionHandlePainter({this.origin}); 202 | 203 | final Offset origin; 204 | 205 | @override 206 | void paint(Canvas canvas, Size size) { 207 | final Paint paint = new Paint() 208 | ..color = _kHandlesColor 209 | ..strokeWidth = 2.0; 210 | // Draw circle below the origin that slightly overlaps the bar. 211 | canvas.drawCircle(origin.translate(0.0, 4.0), 5.5, paint); 212 | // Draw up from origin leaving 10 pixels of margin on top. 213 | canvas.drawLine( 214 | origin, 215 | origin.translate( 216 | 0.0, 217 | -(size.height - 2.0 * _kHandlesPadding), 218 | ), 219 | paint, 220 | ); 221 | } 222 | 223 | @override 224 | bool shouldRepaint(_TextSelectionHandlePainter oldPainter) => 225 | origin != oldPainter.origin; 226 | } 227 | 228 | class _CupertinoTextSelectionControls extends TextSelectionControls { 229 | @override 230 | Size handleSize = _kSelectionOffset; // Used for drag selection offset. 231 | 232 | /// Builder for iOS-style copy/paste text selection toolbar. 233 | @override 234 | Widget buildToolbar(BuildContext context, Rect globalEditableRegion, 235 | Offset position, TextSelectionDelegate delegate) { 236 | assert(debugCheckHasMediaQuery(context)); 237 | return new ConstrainedBox( 238 | constraints: new BoxConstraints.tight(globalEditableRegion.size), 239 | child: new CustomSingleChildLayout( 240 | delegate: new _TextSelectionToolbarLayout( 241 | MediaQuery.of(context).size, 242 | globalEditableRegion, 243 | position, 244 | ), 245 | child: new _TextSelectionToolbar( 246 | delegate: delegate, 247 | handleCut: () => handleCut(delegate), 248 | handleCopy: () => handleCopy(delegate), 249 | handlePaste: () => handlePaste(delegate), 250 | handleSelectAll: () => handleSelectAll(delegate), 251 | ), 252 | )); 253 | } 254 | 255 | /// Builder for iOS text selection edges. 256 | @override 257 | Widget buildHandle(BuildContext context, TextSelectionHandleType type, 258 | double textLineHeight) { 259 | // We want a size that's a vertical line the height of the text plus a 18.0 260 | // padding in every direction that will constitute the selection drag area. 261 | final Size desiredSize = new Size( 262 | 2.0 * _kHandlesPadding, textLineHeight + 2.0 * _kHandlesPadding); 263 | 264 | final Widget handle = new SizedBox.fromSize( 265 | size: desiredSize, 266 | child: new CustomPaint( 267 | painter: new _TextSelectionHandlePainter( 268 | // We give the painter a point of origin that's at the bottom baseline 269 | // of the selection cursor position. 270 | // 271 | // We give it in the form of an offset from the top left of the 272 | // SizedBox. 273 | origin: 274 | new Offset(_kHandlesPadding, textLineHeight + _kHandlesPadding), 275 | ), 276 | ), 277 | ); 278 | 279 | // [buildHandle]'s widget is positioned at the selection cursor's bottom 280 | // baseline. We transform the handle such that the SizedBox is superimposed 281 | // on top of the text selection endpoints. 282 | switch (type) { 283 | case TextSelectionHandleType 284 | .left: // The left handle is upside down on iOS. 285 | return new Transform( 286 | transform: new Matrix4.rotationZ(math.PI) 287 | ..translate(-_kHandlesPadding, -_kHandlesPadding), 288 | child: handle); 289 | case TextSelectionHandleType.right: 290 | return new Transform( 291 | transform: new Matrix4.translationValues( 292 | -_kHandlesPadding, -(textLineHeight + _kHandlesPadding), 0.0), 293 | child: handle); 294 | case TextSelectionHandleType 295 | .collapsed: // iOS doesn't draw anything for collapsed selections. 296 | return new Container(); 297 | } 298 | assert(type != null); 299 | return null; 300 | } 301 | } 302 | 303 | /// Text selection controls that follows iOS design conventions. 304 | final TextSelectionControls cupertinoTextSelectionControls = 305 | new _CupertinoTextSelectionControls(); 306 | -------------------------------------------------------------------------------- /rich_editor/lib/src/services/text_input.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'dart:async'; 6 | import 'dart:ui' show TextAffinity, hashValues; 7 | 8 | import 'package:flutter/foundation.dart'; 9 | import 'package:flutter/material.dart'; 10 | import 'package:flutter/painting.dart'; 11 | import 'package:flutter/services.dart'; 12 | import 'package:flutter_logger/flutter_logger.dart'; 13 | import 'package:rich_editor/src/extensions.dart'; 14 | import 'package:rich_editor/src/services/rich_text_parser.dart'; 15 | import 'package:rich_editor/src/widgets/rich_editable_text.dart'; 16 | 17 | export 'dart:ui' show TextAffinity; 18 | 19 | TextAffinity _toTextAffinity(String affinity) { 20 | switch (affinity) { 21 | case 'TextAffinity.downstream': 22 | return TextAffinity.downstream; 23 | case 'TextAffinity.upstream': 24 | return TextAffinity.upstream; 25 | } 26 | return null; 27 | } 28 | 29 | /// The current text, selection, and composing state for editing a run of text. 30 | @immutable 31 | class RichTextEditingValue extends AbstractTextEditingValue { 32 | /// Creates information for editing a run of text. 33 | /// 34 | /// The selection and composing range must be within the text. 35 | /// 36 | /// The [value], [selection], and [composing] arguments must not be null but 37 | /// each have default values. 38 | const RichTextEditingValue( 39 | {this.value: const TextSpan(text: "", style: StyleController.material), 40 | this.selection: const TextSelection.collapsed(offset: -1), 41 | this.composing: TextRange.empty}) 42 | : assert(value != null), 43 | assert(selection != null), 44 | assert(composing != null); 45 | 46 | /// Creates an instance of this class from a JSON object. 47 | factory RichTextEditingValue.fromJSON( 48 | Map encoded, TextStyle style) { 49 | return new RichTextEditingValue( 50 | value: new TextSpan(text: encoded['text'], style: style), 51 | selection: new TextSelection( 52 | baseOffset: encoded['selectionBase'] ?? -1, 53 | extentOffset: encoded['selectionExtent'] ?? -1, 54 | affinity: _toTextAffinity(encoded['selectionAffinity']) ?? 55 | TextAffinity.downstream, 56 | isDirectional: encoded['selectionIsDirectional'] ?? false, 57 | ), 58 | composing: new TextRange( 59 | start: encoded['composingBase'] ?? -1, 60 | end: encoded['composingExtent'] ?? -1, 61 | ), 62 | ); 63 | } 64 | 65 | /// The current text being edited. 66 | final TextSpan value; 67 | 68 | /// The range of text that is currently selected. 69 | final TextSelection selection; 70 | 71 | /// The range of text that is still being composed. 72 | final TextRange composing; 73 | 74 | /// A value that corresponds to the empty string with no selection and no composing range. 75 | static const RichTextEditingValue empty = const RichTextEditingValue(); 76 | 77 | /// Creates a copy of this value but with the given fields replaced with the new values. 78 | @override 79 | AbstractTextEditingValue copyWith( 80 | {TextSpan value, TextSelection selection, TextRange composing}) { 81 | return new RichTextEditingValue( 82 | value: value ?? this.value, 83 | selection: selection ?? this.selection, 84 | composing: composing ?? this.composing); 85 | } 86 | 87 | @override 88 | String getSelectedText() => selection.textInside(value.toPlainText()); 89 | 90 | @override 91 | TextSpan getUnselectedText() { 92 | String plainText = value.toPlainText(); 93 | 94 | String newText = 95 | selection.textBefore(plainText) + selection.textAfter(plainText); 96 | 97 | return RichTextEditingValueParser 98 | .parse( 99 | oldValue: this, 100 | newValue: copyWith(value: new TextSpan(text: newText)), 101 | style: null, 102 | ) 103 | .value; 104 | } 105 | 106 | @override 107 | AbstractTextEditingValue insert(String text) { 108 | String plainText = value.toPlainText(); 109 | 110 | String newText = 111 | selection.textBefore(plainText) + text + selection.textAfter(plainText); 112 | 113 | print(newText); 114 | return RichTextEditingValueParser.parse( 115 | oldValue: this, 116 | newValue: copyWith(value: new TextSpan(text: newText)), 117 | style: null, 118 | ); 119 | } 120 | 121 | @override 122 | int get length => Extensions.length(value); 123 | 124 | @override 125 | bool get isNotEmpty => Extensions.isNotEmpty(value); 126 | 127 | @override 128 | String get text => value.toPlainText(); 129 | 130 | @override 131 | String toString() => 132 | '$runtimeType(textSpan: \u2524$value\u251C, selection: $selection, composing: $composing)'; 133 | 134 | @override 135 | bool operator ==(dynamic other) { 136 | if (identical(this, other)) return true; 137 | if (other is! RichTextEditingValue) return false; 138 | final RichTextEditingValue typedOther = other; 139 | return typedOther.value == value && 140 | typedOther.selection == selection && 141 | typedOther.composing == composing; 142 | } 143 | 144 | @override 145 | int get hashCode => 146 | hashValues(value.hashCode, selection.hashCode, composing.hashCode); 147 | } 148 | 149 | abstract class AbstractTextEditingValue { 150 | const AbstractTextEditingValue({this.value, this.selection, this.composing}); 151 | 152 | /// Creates a copy of this value but with the given fields replaced with the new values. 153 | AbstractTextEditingValue copyWith( 154 | {Value value, TextSelection selection, TextRange composing}); 155 | 156 | /// Returns a representation of this object as a JSON object. 157 | Map toJSON() { 158 | return { 159 | 'text': text, 160 | 'selectionBase': selection.baseOffset, 161 | 'selectionExtent': selection.extentOffset, 162 | 'selectionAffinity': selection.affinity.toString(), 163 | 'selectionIsDirectional': selection.isDirectional, 164 | 'composingBase': composing.start, 165 | 'composingExtent': composing.end, 166 | }; 167 | } 168 | 169 | /// The current text being edited. 170 | final Value value; 171 | 172 | /// The range of text that is currently selected. 173 | final TextSelection selection; 174 | 175 | /// The range of text that is still being composed. 176 | final TextRange composing; 177 | 178 | String getSelectedText(); 179 | 180 | Value getUnselectedText(); 181 | 182 | AbstractTextEditingValue insert(String text); 183 | 184 | int get length; 185 | 186 | bool get isNotEmpty; 187 | 188 | String get text; 189 | 190 | @override 191 | String toString() => 192 | '$runtimeType(value: \u2524$value\u251C, selection: $selection, composing: $composing)'; 193 | 194 | @override 195 | bool operator ==(dynamic other) { 196 | if (identical(this, other)) return true; 197 | if (other is! Value) return false; 198 | final AbstractTextEditingValue typedOther = other; 199 | return typedOther.value == value && 200 | typedOther.selection == selection && 201 | typedOther.composing == composing; 202 | } 203 | 204 | @override 205 | int get hashCode => 206 | hashValues(value.hashCode, selection.hashCode, composing.hashCode); 207 | } 208 | 209 | /// An interface to receive information from [TextInput]. 210 | /// 211 | /// See also: 212 | /// 213 | /// * [TextInput.attach] 214 | abstract class TextInputClient { 215 | /// Abstract const constructor. This constructor enables subclasses to provide 216 | /// const constructors so that they can be used in const expressions. 217 | const TextInputClient(); 218 | 219 | /// Requests that this client update its editing state to the given value. 220 | void updateEditingValue(T value); 221 | 222 | /// Requests that this client perform the given action. 223 | void performAction(TextInputAction action); 224 | 225 | /// Return the concert implementation of this clients value/ 226 | T getValue(Map encoded); 227 | } 228 | 229 | /// A interface for interacting with a text input control. 230 | /// 231 | /// See also: 232 | /// 233 | /// * [TextInput.attach] 234 | class TextInputConnection { 235 | final Log log = new Log("TextInputConnection"); 236 | 237 | TextInputConnection._(this._client) 238 | : assert(_client != null), 239 | _id = _nextId++; 240 | 241 | static int _nextId = 1; 242 | final int _id; 243 | 244 | final TextInputClient _client; 245 | 246 | /// Whether this connection is currently interacting with the text input control. 247 | bool get attached => _clientHandler._currentConnection == this; 248 | 249 | /// Requests that the text input control become visible. 250 | void show() { 251 | assert(attached); 252 | SystemChannels.textInput.invokeMethod('TextInput.show'); 253 | } 254 | 255 | /// Requests that the text input control change its internal state to match the given state. 256 | void setEditingState(AbstractTextEditingValue value) { 257 | log.d("setEditingState: ${value.toJSON()}"); 258 | assert(attached); 259 | SystemChannels.textInput.invokeMethod( 260 | 'TextInput.setEditingState', 261 | value.toJSON(), 262 | ); 263 | } 264 | 265 | /// Stop interacting with the text input control. 266 | /// 267 | /// After calling this method, the text input control might disappear if no 268 | /// other client attaches to it within this animation frame. 269 | void close() { 270 | if (attached) { 271 | SystemChannels.textInput.invokeMethod('TextInput.clearClient'); 272 | _clientHandler 273 | .._currentConnection = null 274 | .._scheduleHide(); 275 | } 276 | assert(!attached); 277 | } 278 | } 279 | 280 | TextInputAction _toTextInputAction(String action) { 281 | switch (action) { 282 | case 'TextInputAction.done': 283 | return TextInputAction.done; 284 | case 'TextInputAction.newline': 285 | return TextInputAction.newline; 286 | } 287 | throw new FlutterError('Unknown text input action: $action'); 288 | } 289 | 290 | class _TextInputClientHandler { 291 | _TextInputClientHandler() { 292 | SystemChannels.textInput.setMethodCallHandler(_handleTextInputInvocation); 293 | } 294 | 295 | TextInputConnection _currentConnection; 296 | 297 | Future _handleTextInputInvocation(MethodCall methodCall) async { 298 | if (_currentConnection == null) return; 299 | final String method = methodCall.method; 300 | final List args = methodCall.arguments; 301 | final int client = args[0]; 302 | // The incoming message was for a different client. 303 | if (client != _currentConnection._id) return; 304 | switch (method) { 305 | case 'TextInputClient.updateEditingState': 306 | var value = _currentConnection._client.getValue(args[1]); 307 | _currentConnection._client.updateEditingValue(value); 308 | break; 309 | case 'TextInputClient.performAction': 310 | _currentConnection._client.performAction(_toTextInputAction(args[1])); 311 | break; 312 | default: 313 | throw new MissingPluginException(); 314 | } 315 | } 316 | 317 | bool _hidePending = false; 318 | 319 | void _scheduleHide() { 320 | if (_hidePending) return; 321 | _hidePending = true; 322 | 323 | // Schedule a deferred task that hides the text input. If someone else 324 | // shows the keyboard during this update cycle, then the task will do 325 | // nothing. 326 | scheduleMicrotask(() { 327 | _hidePending = false; 328 | if (_currentConnection == null) 329 | SystemChannels.textInput.invokeMethod('TextInput.hide'); 330 | }); 331 | } 332 | } 333 | 334 | final _TextInputClientHandler _clientHandler = new _TextInputClientHandler(); 335 | 336 | /// An interface to the system's text input control. 337 | class TextInput { 338 | TextInput._(); 339 | 340 | /// Begin interacting with the text input control. 341 | /// 342 | /// Calling this function helps multiple clients coordinate about which one is 343 | /// currently interacting with the text input control. The returned 344 | /// [TextInputConnection] provides an interface for actually interacting with 345 | /// the text input control. 346 | /// 347 | /// A client that no longer wishes to interact with the text input control 348 | /// should call [TextInputConnection.close] on the returned 349 | /// [TextInputConnection]. 350 | static TextInputConnection attach( 351 | TextInputClient client, TextInputConfiguration configuration) { 352 | assert(client != null); 353 | assert(configuration != null); 354 | final TextInputConnection connection = new TextInputConnection._(client); 355 | _clientHandler._currentConnection = connection; 356 | SystemChannels.textInput.invokeMethod( 357 | 'TextInput.setClient', 358 | [connection._id, configuration.toJSON()], 359 | ); 360 | return connection; 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /rich_editor/lib/src/material/rich_text_field.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:flutter/cupertino.dart' hide cupertinoTextSelectionControls; 6 | import 'package:flutter/foundation.dart'; 7 | import 'package:flutter/material.dart' hide materialTextSelectionControls; 8 | import 'package:flutter/services.dart'; 9 | import 'package:flutter/widgets.dart'; 10 | import 'package:flutter_logger/flutter_logger.dart'; 11 | import 'package:rich_editor/src/cupertino/text_selection.dart'; 12 | import 'package:rich_editor/src/extensions.dart'; 13 | import 'package:rich_editor/src/material/text_selection.dart'; 14 | import 'package:rich_editor/src/widgets/rich_editable_text.dart'; 15 | 16 | export 'package:flutter/services.dart' show TextInputType; 17 | 18 | const Duration _kTransitionDuration = const Duration(milliseconds: 200); 19 | const Curve _kTransitionCurve = Curves.fastOutSlowIn; 20 | 21 | /// A material design text field. 22 | /// 23 | /// A text field lets the user enter text, either with hardware keyboard or with 24 | /// an onscreen keyboard. 25 | /// 26 | /// The text field calls the [onChanged] callback whenever the user changes the 27 | /// text in the field. If the user indicates that they are done typing in the 28 | /// field (e.g., by pressing a button on the soft keyboard), the text field 29 | /// calls the [onSubmitted] callback. 30 | /// 31 | /// To control the text that is displayed in the text field, use the 32 | /// [controller]. For example, to set the initial value of the text field, use 33 | /// a [controller] that already contains some text. The [controller] can also 34 | /// control the selection and composing region (and to observe changes to the 35 | /// text, selection, and composing region). 36 | /// 37 | /// By default, a text field has a [decoration] that draws a divider below the 38 | /// text field. You can use the [decoration] property to control the decoration, 39 | /// for example by adding a label or an icon. If you set the [decoration] 40 | /// property to null, the decoration will be removed entirely, including the 41 | /// extra padding introduced by the decoration to save space for the labels. 42 | /// 43 | /// If [decoration] is non-null (which is the default), the text field requires 44 | /// one of its ancestors to be a [Material] widget. 45 | /// 46 | /// To integrate the [RichTextField] into a [Form] with other [FormField] widgets, 47 | /// consider using [TextFormField]. 48 | /// 49 | /// See also: 50 | /// 51 | /// * 52 | /// * [TextFormField], which integrates with the [Form] widget. 53 | /// * [InputDecorator], which shows the labels and other visual elements that 54 | /// surround the actual text editing widget. 55 | /// * [EditableText], which is the raw text editing control at the heart of a 56 | /// [TextField]. (The [EditableText] widget is rarely used directly unless 57 | /// you are implementing an entirely different design language, such as 58 | /// Cupertino.) 59 | class RichTextField extends StatefulWidget { 60 | /// Creates a Material Design text field. 61 | /// 62 | /// If [decoration] is non-null (which is the default), the text field requires 63 | /// one of its ancestors to be a [Material] widget. 64 | /// 65 | /// To remove the decoration entirely (including the extra padding introduced 66 | /// by the decoration to save space for the labels), set the [decoration] to 67 | /// null. 68 | /// 69 | /// The [maxLines] property can be set to null to remove the restriction on 70 | /// the number of lines. By default, it is one, meaning this is a single-line 71 | /// text field. [maxLines] must not be zero. If [maxLines] is not one, then 72 | /// [keyboardType] is ignored, and the [TextInputType.multiline] keyboard type 73 | /// is used. 74 | /// 75 | /// The [keyboardType], [textAlign], [autofocus], and 76 | /// [autocorrect] arguments must not be null. 77 | const RichTextField({ 78 | Key key, 79 | this.controller, 80 | this.styleController, 81 | this.focusNode, 82 | this.decoration: const InputDecoration(), 83 | TextInputType keyboardType: TextInputType.text, 84 | this.style, 85 | this.textAlign: TextAlign.start, 86 | this.autofocus: false, 87 | this.autocorrect: true, 88 | this.maxLines: 1, 89 | this.onChanged, 90 | this.onSubmitted, 91 | }) 92 | : assert(keyboardType != null), 93 | assert(textAlign != null), 94 | assert(autofocus != null), 95 | assert(autocorrect != null), 96 | assert(maxLines == null || maxLines > 0), 97 | keyboardType = maxLines == 1 ? keyboardType : TextInputType.multiline, 98 | super(key: key); 99 | 100 | /// Controls the text being edited. 101 | /// 102 | /// If null, this widget will create its own [TextEditingController]. 103 | final RichTextEditingController controller; 104 | 105 | /// Control the style of the [RichTextField] as the user types. 106 | /// 107 | /// If null the [RichTextField] will act as a normal [TextField]. 108 | final StyleController styleController; 109 | 110 | /// Controls whether this widget has keyboard focus. 111 | /// 112 | /// If null, this widget will create its own [FocusNode]. 113 | final FocusNode focusNode; 114 | 115 | /// The decoration to show around the text field. 116 | /// 117 | /// By default, draws a horizontal line under the text field but can be 118 | /// configured to show an icon, label, hint text, and error text. 119 | /// 120 | /// Set this field to null to remove the decoration entirely (including the 121 | /// extra padding introduced by the decoration to save space for the labels). 122 | final InputDecoration decoration; 123 | 124 | /// The type of keyboard to use for editing the text. 125 | /// 126 | /// Defaults to [TextInputType.text]. Must not be null. If 127 | /// [maxLines] is not one, then [keyboardType] is ignored, and the 128 | /// [TextInputType.multiline] keyboard type is used. 129 | final TextInputType keyboardType; 130 | 131 | /// The style to use for the text being edited. 132 | /// 133 | /// This text style is also used as the base style for the [decoration]. 134 | /// 135 | /// If null, defaults to a text style from the current [Theme]. 136 | final TextStyle style; 137 | 138 | /// How the text being edited should be aligned horizontally. 139 | /// 140 | /// Defaults to [TextAlign.start]. 141 | final TextAlign textAlign; 142 | 143 | /// Whether this text field should focus itself if nothing else is already 144 | /// focused. 145 | /// 146 | /// If true, the keyboard will open as soon as this text field obtains focus. 147 | /// Otherwise, the keyboard is only shown after the user taps the text field. 148 | /// 149 | /// Defaults to false. Cannot be null. 150 | // See https://github.com/flutter/flutter/issues/7035 for the rationale for this 151 | // keyboard behavior. 152 | final bool autofocus; 153 | 154 | /// Whether to enable autocorrection. 155 | /// 156 | /// Defaults to true. Cannot be null. 157 | final bool autocorrect; 158 | 159 | /// The maximum number of lines for the text to span, wrapping if necessary. 160 | /// 161 | /// If this is 1 (the default), the text will not wrap, but will scroll 162 | /// horizontally instead. 163 | /// 164 | /// If this is null, there is no limit to the number of lines. If it is not 165 | /// null, the value must be greater than zero. 166 | final int maxLines; 167 | 168 | /// Called when the text being edited changes. 169 | final ValueChanged onChanged; 170 | 171 | /// Called when the user indicates that they are done editing the text in the 172 | /// field. 173 | final ValueChanged onSubmitted; 174 | 175 | void setNewStyle(TextStyle textStyle) {} 176 | 177 | @override 178 | RichTextFieldState createState() => new RichTextFieldState(); 179 | 180 | @override 181 | void debugFillProperties(DiagnosticPropertiesBuilder description) { 182 | super.debugFillProperties(description); 183 | description.add(new DiagnosticsProperty( 184 | 'controller', controller, 185 | defaultValue: null)); 186 | description.add(new DiagnosticsProperty('focusNode', focusNode, 187 | defaultValue: null)); 188 | description.add( 189 | new DiagnosticsProperty('decoration', decoration)); 190 | description.add(new EnumProperty( 191 | 'keyboardType', keyboardType, 192 | defaultValue: TextInputType.text)); 193 | description.add( 194 | new DiagnosticsProperty('style', style, defaultValue: null)); 195 | description.add(new DiagnosticsProperty('autofocus', autofocus, 196 | defaultValue: false)); 197 | description.add(new DiagnosticsProperty('autocorrect', autocorrect, 198 | defaultValue: false)); 199 | description.add(new IntProperty('maxLines', maxLines, defaultValue: 1)); 200 | } 201 | } 202 | 203 | class RichTextFieldState extends State { 204 | final Log log = new Log("_TextFieldState"); 205 | 206 | final GlobalKey _editableTextKey = 207 | new GlobalKey(); 208 | 209 | RichTextEditingController _controller; 210 | 211 | RichTextEditingController get _effectiveController => 212 | widget.controller ?? _controller; 213 | 214 | FocusNode _focusNode; 215 | 216 | FocusNode get _effectiveFocusNode => 217 | widget.focusNode ?? (_focusNode ??= new FocusNode()); 218 | 219 | @override 220 | void initState() { 221 | super.initState(); 222 | if (widget.controller == null) 223 | _controller = new RichTextEditingController(); 224 | } 225 | 226 | @override 227 | void didUpdateWidget(RichTextField oldWidget) { 228 | super.didUpdateWidget(oldWidget); 229 | if (widget.controller == null && oldWidget.controller != null) 230 | _controller = 231 | new RichTextEditingController.fromValue(oldWidget.controller.value); 232 | else if (widget.controller != null && oldWidget.controller == null) 233 | _controller = null; 234 | } 235 | 236 | @override 237 | void dispose() { 238 | _focusNode?.dispose(); 239 | super.dispose(); 240 | } 241 | 242 | void _requestKeyboard() { 243 | _editableTextKey.currentState?.requestKeyboard(); 244 | } 245 | 246 | void prepareForFocusLoss({bool closeKeyboardIfNeeded = false}) { 247 | _editableTextKey.currentState?.saveValueBeforeFocusLoss = true; 248 | _editableTextKey.currentState?.closeKeyboardIfNeeded = closeKeyboardIfNeeded; 249 | } 250 | 251 | void restoreFocus() { 252 | _editableTextKey.currentState?.requestFocus(); 253 | } 254 | 255 | void _onSelectionChanged( 256 | BuildContext context, TextSelection selection, bool longPress) { 257 | log.d("_onSelectionChanged $selection"); 258 | if (longPress) Feedback.forLongPress(context); 259 | } 260 | 261 | @override 262 | Widget build(BuildContext context) { 263 | final ThemeData themeData = Theme.of(context); 264 | final TextStyle style = widget.style ?? themeData.textTheme.body1; 265 | final RichTextEditingController controller = _effectiveController; 266 | final FocusNode focusNode = _effectiveFocusNode; 267 | 268 | Widget child = new RepaintBoundary( 269 | child: new RichEditableText( 270 | key: _editableTextKey, 271 | controller: controller, 272 | styleController: widget.styleController, 273 | focusNode: focusNode, 274 | keyboardType: widget.keyboardType, 275 | style: style, 276 | textAlign: widget.textAlign, 277 | autofocus: widget.autofocus, 278 | autocorrect: widget.autocorrect, 279 | maxLines: widget.maxLines, 280 | cursorColor: themeData.textSelectionColor, 281 | selectionColor: themeData.textSelectionColor, 282 | selectionControls: themeData.platform == TargetPlatform.iOS 283 | ? cupertinoTextSelectionControls 284 | : materialTextSelectionControls, 285 | onChanged: widget.onChanged, 286 | onSubmitted: widget.onSubmitted, 287 | onSelectionChanged: (TextSelection selection, bool longPress) => 288 | _onSelectionChanged(context, selection, longPress)), 289 | ); 290 | 291 | if (widget.decoration != null) { 292 | child = new AnimatedBuilder( 293 | animation: new Listenable.merge([focusNode, controller]), 294 | builder: (BuildContext context, Widget child) { 295 | return new InputDecorator( 296 | decoration: widget.decoration, 297 | baseStyle: widget.style, 298 | textAlign: widget.textAlign, 299 | isFocused: focusNode.hasFocus, 300 | isEmpty: Extensions.isEmpty(controller.value.value), 301 | child: child, 302 | ); 303 | }, 304 | child: child, 305 | ); 306 | } 307 | 308 | return new GestureDetector( 309 | behavior: HitTestBehavior.opaque, 310 | onTap: _requestKeyboard, 311 | child: child, 312 | ); 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /rich_editor/lib/src/widgets/format_toolbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart' hide DropdownButton, DropdownMenuItem; 3 | import 'package:flutter_logger/flutter_logger.dart'; 4 | import 'package:material_color_picker/material_color_picker.dart'; 5 | import 'package:rich_editor/src/extensions.dart'; 6 | import 'package:rich_editor/src/material/dropdown.dart'; 7 | import 'package:rich_editor/src/material/rich_text_field.dart'; 8 | import 'package:rich_editor/src/widgets/rich_editable_text.dart'; 9 | 10 | const double _defaultFontSize = 16.0; 11 | const FontItem _defaultFont = const FontItem( 12 | name: "Roboto", 13 | weights: const [100, 200, 300, 400, 500, 600, 700, 800, 900], 14 | ); 15 | const Color _defaultColor = Colors.black87; 16 | 17 | class FormatToolbar extends StatefulWidget { 18 | FormatToolbar({ 19 | @required StyleController styleController, 20 | @required GlobalKey richTextFieldState, 21 | }) 22 | : _styleController = styleController, 23 | _richTextFieldState = richTextFieldState; 24 | 25 | final StyleController _styleController; 26 | final GlobalKey _richTextFieldState; 27 | 28 | @override 29 | State createState() => new _FormatToolbarState(); 30 | } 31 | 32 | class _FormatToolbarState extends State { 33 | final Log log = new Log("_FormatToolbarState"); 34 | final GlobalKey<_FontSizeWidgetState> _fontSizeState = 35 | new GlobalKey<_FontSizeWidgetState>(); 36 | 37 | StyleController _styleController; 38 | TextStyle _lastKnownStyle; 39 | 40 | bool _bold = false; 41 | bool _italic = false; 42 | bool _underline = false; 43 | bool _lineThrough = false; 44 | bool _overline = false; 45 | double _size = _defaultFontSize; 46 | String _fontName = "Roboto"; 47 | Color _textColor = _defaultColor; 48 | 49 | @override 50 | void initState() { 51 | super.initState(); 52 | _styleController = widget._styleController; 53 | _styleController.addListener(_onStyleChanged); 54 | 55 | _lastKnownStyle = _styleController.value; 56 | _size = widget._styleController.value.fontSize; 57 | } 58 | 59 | @override 60 | void didUpdateWidget(FormatToolbar oldWidget) { 61 | super.didUpdateWidget(oldWidget); 62 | if (widget._styleController != oldWidget._styleController) { 63 | oldWidget._styleController.removeListener(_onStyleChanged); 64 | widget._styleController.addListener(_onStyleChanged); 65 | } 66 | } 67 | 68 | @override 69 | void dispose() { 70 | super.dispose(); 71 | _styleController.removeListener(_onStyleChanged); 72 | } 73 | 74 | void _setFont(FontItem value) { 75 | log.d(value); 76 | _styleController.value = Extensions.copyStyleWith( 77 | base: _styleController.value, 78 | fontFamily: value.name, 79 | package: "rich_editor", 80 | ); 81 | setState(() => _fontName = value.name); 82 | } 83 | 84 | _setTextColor(Color color) { 85 | widget._richTextFieldState.currentState.restoreFocus(); 86 | if (color == null) return; 87 | 88 | _styleController.value = _styleController.value.copyWith(color: color); 89 | setState(() => _textColor = color); 90 | } 91 | 92 | void _setFontSize(double size) { 93 | _styleController.value = _styleController.value.copyWith(fontSize: size); 94 | setState(() => _size = size); 95 | } 96 | 97 | void _setBold() { 98 | var fontWeight = _lastKnownStyle.fontWeight == FontWeight.bold 99 | ? FontWeight.normal 100 | : FontWeight.bold; 101 | 102 | _styleController.value = 103 | _styleController.value.copyWith(fontWeight: fontWeight); 104 | 105 | setState(() { 106 | _bold = fontWeight == FontWeight.bold; 107 | }); 108 | } 109 | 110 | void _setItalic() { 111 | var fontStyle = _lastKnownStyle.fontStyle == FontStyle.italic 112 | ? FontStyle.normal 113 | : FontStyle.italic; 114 | 115 | _styleController.value = 116 | _styleController.value.copyWith(fontStyle: fontStyle); 117 | 118 | setState(() { 119 | _italic = fontStyle == FontStyle.italic; 120 | }); 121 | } 122 | 123 | void _setUnderlined() { 124 | var underlineDecoration; 125 | var lineThroughDecoration; 126 | var overlineDecoration; 127 | 128 | if (_lastKnownStyle.decoration != null) { 129 | underlineDecoration = 130 | _lastKnownStyle.decoration.contains(TextDecoration.underline) 131 | ? TextDecoration.none 132 | : TextDecoration.underline; 133 | 134 | lineThroughDecoration = 135 | _lastKnownStyle.decoration.contains(TextDecoration.lineThrough) 136 | ? TextDecoration.lineThrough 137 | : TextDecoration.none; 138 | 139 | overlineDecoration = 140 | _lastKnownStyle.decoration.contains(TextDecoration.overline) 141 | ? TextDecoration.overline 142 | : TextDecoration.none; 143 | } else { 144 | underlineDecoration = TextDecoration.underline; 145 | lineThroughDecoration = TextDecoration.none; 146 | overlineDecoration = TextDecoration.none; 147 | } 148 | 149 | var textDecoration = new TextDecoration.combine( 150 | [underlineDecoration, lineThroughDecoration, overlineDecoration], 151 | ); 152 | 153 | _styleController.value = 154 | _styleController.value.copyWith(decoration: textDecoration); 155 | 156 | setState(() { 157 | _underline = textDecoration.contains(TextDecoration.underline); 158 | }); 159 | } 160 | 161 | void _setLineThrough() { 162 | var underlineDecoration; 163 | var lineThroughDecoration; 164 | var overlineDecoration; 165 | 166 | if (_lastKnownStyle.decoration != null) { 167 | underlineDecoration = 168 | _lastKnownStyle.decoration.contains(TextDecoration.underline) 169 | ? TextDecoration.underline 170 | : TextDecoration.none; 171 | 172 | lineThroughDecoration = 173 | _lastKnownStyle.decoration.contains(TextDecoration.lineThrough) 174 | ? TextDecoration.none 175 | : TextDecoration.lineThrough; 176 | 177 | overlineDecoration = 178 | _lastKnownStyle.decoration.contains(TextDecoration.overline) 179 | ? TextDecoration.overline 180 | : TextDecoration.none; 181 | } else { 182 | underlineDecoration = TextDecoration.none; 183 | lineThroughDecoration = TextDecoration.lineThrough; 184 | overlineDecoration = TextDecoration.none; 185 | } 186 | 187 | var textDecoration = new TextDecoration.combine( 188 | [underlineDecoration, lineThroughDecoration, overlineDecoration], 189 | ); 190 | 191 | _styleController.value = 192 | _styleController.value.copyWith(decoration: textDecoration); 193 | 194 | setState(() { 195 | _lineThrough = textDecoration.contains(TextDecoration.lineThrough); 196 | }); 197 | } 198 | 199 | void _setOverline() { 200 | var underlineDecoration; 201 | var lineThroughDecoration; 202 | var overlineDecoration; 203 | 204 | if (_lastKnownStyle.decoration != null) { 205 | underlineDecoration = 206 | _lastKnownStyle.decoration.contains(TextDecoration.underline) 207 | ? TextDecoration.underline 208 | : TextDecoration.none; 209 | 210 | lineThroughDecoration = 211 | _lastKnownStyle.decoration.contains(TextDecoration.lineThrough) 212 | ? TextDecoration.lineThrough 213 | : TextDecoration.none; 214 | 215 | overlineDecoration = 216 | _lastKnownStyle.decoration.contains(TextDecoration.overline) 217 | ? TextDecoration.none 218 | : TextDecoration.overline; 219 | } else { 220 | underlineDecoration = TextDecoration.none; 221 | lineThroughDecoration = TextDecoration.none; 222 | overlineDecoration = TextDecoration.overline; 223 | } 224 | 225 | var textDecoration = new TextDecoration.combine( 226 | [underlineDecoration, lineThroughDecoration, overlineDecoration], 227 | ); 228 | 229 | _styleController.value = 230 | _styleController.value.copyWith(decoration: textDecoration); 231 | 232 | setState(() { 233 | _overline = textDecoration.contains(TextDecoration.overline); 234 | }); 235 | } 236 | 237 | void _onDropdownEvent(OnTapState value) { 238 | switch (value) { 239 | case OnTapState.START: 240 | widget._richTextFieldState.currentState.prepareForFocusLoss(); 241 | break; 242 | 243 | case OnTapState.END: 244 | widget._richTextFieldState.currentState.restoreFocus(); 245 | break; 246 | } 247 | } 248 | 249 | void _onStyleChanged() { 250 | log.d("_FormatToolbarState: $_lastKnownStyle"); 251 | log.d("_FormatToolbarState: ${_styleController.value}"); 252 | _lastKnownStyle = _styleController.value; 253 | 254 | setState(() { 255 | _bold = _isBold(); 256 | _italic = _isItalic(); 257 | _underline = _isUnderlined(); 258 | _lineThrough = _isLineThrough(); 259 | _overline = _isOverlined(); 260 | _size = _lastKnownStyle.fontSize; 261 | _fontSizeState.currentState.setSize(_lastKnownStyle.fontSize); 262 | _fontName = () { 263 | // fontFamily come in this form > packages/rich_editor/Berkshire Swash 264 | var font = _lastKnownStyle.fontFamily; 265 | log.d(font); 266 | var index = font?.lastIndexOf("/"); 267 | if (index != -1) 268 | return font?.substring(index + 1); 269 | else 270 | return font; 271 | }(); 272 | _textColor = _lastKnownStyle.color; 273 | }); 274 | } 275 | 276 | bool _isBold() { 277 | if (_lastKnownStyle.fontWeight == FontWeight.bold) 278 | return true; 279 | else 280 | return false; 281 | } 282 | 283 | bool _isItalic() { 284 | if (_lastKnownStyle.fontStyle == FontStyle.italic) 285 | return true; 286 | else 287 | return false; 288 | } 289 | 290 | bool _isUnderlined() { 291 | if (_lastKnownStyle.decoration == null) return false; 292 | if (_lastKnownStyle.decoration.contains(TextDecoration.underline)) 293 | return true; 294 | else 295 | return false; 296 | } 297 | 298 | bool _isLineThrough() { 299 | if (_lastKnownStyle.decoration == null) return false; 300 | if (_lastKnownStyle.decoration.contains(TextDecoration.lineThrough)) 301 | return true; 302 | else 303 | return false; 304 | } 305 | 306 | bool _isOverlined() { 307 | if (_lastKnownStyle.decoration == null) return false; 308 | if (_lastKnownStyle.decoration.contains(TextDecoration.overline)) 309 | return true; 310 | else 311 | return false; 312 | } 313 | 314 | @override 315 | Widget build(BuildContext context) { 316 | List childrenList = [ 317 | new FontSizeWidget( 318 | key: _fontSizeState, 319 | size: _size, 320 | onChange: _setFontSize, 321 | ), 322 | new DropdownButton( 323 | items: fonts, 324 | onTap: _onDropdownEvent, 325 | onChanged: _setFont, 326 | value: fontsMap[_fontName], 327 | ), 328 | new ColorPickerButton( 329 | button: new Center( 330 | child: new Icon( 331 | Icons.format_color_text, 332 | color: _textColor, 333 | ), 334 | ), 335 | currentColor: _textColor, 336 | onColor: _setTextColor, 337 | onShow: (type) { 338 | widget._richTextFieldState.currentState.prepareForFocusLoss( 339 | closeKeyboardIfNeeded: 340 | type == DisplayType.bottomSheet ? true : false); 341 | }, 342 | ), 343 | new IconButton( 344 | onPressed: _setBold, 345 | icon: new Icon( 346 | Icons.format_bold, 347 | ), 348 | color: _bold ? Theme.of(context).primaryColor : null, 349 | ), 350 | new IconButton( 351 | onPressed: _setItalic, 352 | icon: new Icon( 353 | Icons.format_italic, 354 | ), 355 | color: _italic ? Theme.of(context).primaryColor : null, 356 | ), 357 | new IconButton( 358 | onPressed: _setUnderlined, 359 | icon: new Icon( 360 | Icons.format_underlined, 361 | ), 362 | color: _underline ? Theme.of(context).primaryColor : null, 363 | ), 364 | new IconButton( 365 | onPressed: _setLineThrough, 366 | icon: new Icon( 367 | Icons.format_strikethrough, 368 | ), 369 | color: _lineThrough ? Theme.of(context).primaryColor : null, 370 | ), 371 | new IconButton( 372 | onPressed: _setOverline, 373 | icon: new ImageIcon(new AssetImage("res/images/format_overline.png", 374 | package: "rich_editor")), 375 | color: _overline ? Theme.of(context).primaryColor : null, 376 | ), 377 | ]; 378 | 379 | return new Container( 380 | alignment: Alignment.bottomCenter, 381 | height: 48.0, 382 | decoration: new BoxDecoration( 383 | color: new Color(0xFFE8E8E8), 384 | ), 385 | child: new ListView( 386 | scrollDirection: Axis.horizontal, 387 | children: childrenList, 388 | ), 389 | ); 390 | } 391 | } 392 | 393 | const List> fonts = 394 | const >[ 395 | const DropdownMenuItem( 396 | child: const Text( 397 | "Roboto", 398 | style: const TextStyle( 399 | color: Colors.black, 400 | fontFamily: "Roboto", 401 | ), 402 | ), 403 | value: _defaultFont, 404 | ), 405 | const DropdownMenuItem( 406 | child: const Text( 407 | "Berkshire Swash", 408 | style: const TextStyle( 409 | color: Colors.black, 410 | fontFamily: "Berkshire Swash", 411 | package: "rich_editor", 412 | ), 413 | ), 414 | value: const FontItem( 415 | name: "Berkshire Swash", 416 | weights: const [400], 417 | ), 418 | ), 419 | const DropdownMenuItem( 420 | child: const Text( 421 | "Cinzel Decorative", 422 | style: const TextStyle( 423 | color: Colors.black, 424 | fontFamily: "Cinzel Decorative", 425 | package: "rich_editor", 426 | ), 427 | ), 428 | value: const FontItem( 429 | name: "Cinzel Decorative", 430 | weights: const [400, 700, 900], 431 | ), 432 | ), 433 | const DropdownMenuItem( 434 | child: const Text( 435 | "Comfortaa", 436 | style: const TextStyle( 437 | color: Colors.black, 438 | fontFamily: "Comfortaa", 439 | package: "rich_editor", 440 | ), 441 | ), 442 | value: const FontItem( 443 | name: "Comfortaa", 444 | weights: const [300, 400, 700], 445 | ), 446 | ), 447 | const DropdownMenuItem( 448 | child: const Text( 449 | "Indie Flower", 450 | style: const TextStyle( 451 | color: Colors.black, 452 | fontFamily: "Indie Flower", 453 | package: "rich_editor", 454 | fontSize: 18.0), 455 | ), 456 | value: const FontItem( 457 | name: "Indie Flower", 458 | weights: const [400], 459 | ), 460 | ), 461 | const DropdownMenuItem( 462 | child: const Text( 463 | "Lobster", 464 | style: const TextStyle( 465 | color: Colors.black, 466 | fontFamily: "Lobster", 467 | package: "rich_editor", 468 | ), 469 | ), 470 | value: const FontItem( 471 | name: "Lobster", 472 | weights: const [400], 473 | ), 474 | ), 475 | const DropdownMenuItem( 476 | child: const Text( 477 | "Satisfy", 478 | style: const TextStyle( 479 | color: Colors.black, 480 | fontFamily: "Satisfy", 481 | package: "rich_editor", 482 | ), 483 | ), 484 | value: const FontItem( 485 | name: "Satisfy", 486 | weights: const [400], 487 | ), 488 | ), 489 | const DropdownMenuItem( 490 | child: const Text( 491 | "Tangerine", 492 | style: const TextStyle( 493 | color: Colors.black, 494 | fontFamily: "Tangerine", 495 | package: "rich_editor", 496 | fontSize: 26.0, 497 | fontWeight: FontWeight.w700, 498 | ), 499 | ), 500 | value: const FontItem( 501 | name: "Tangerine", 502 | weights: const [400, 700], 503 | ), 504 | ), 505 | ]; 506 | 507 | const Map fontsMap = const { 508 | "Roboto": _defaultFont, 509 | "Berkshire Swash": const FontItem( 510 | name: "Berkshire Swash", 511 | weights: const [400], 512 | ), 513 | "Cinzel Decorative": const FontItem( 514 | name: "Cinzel Decorative", 515 | weights: const [400, 700, 900], 516 | ), 517 | "Comfortaa": const FontItem( 518 | name: "Comfortaa", 519 | weights: const [300, 400, 700], 520 | ), 521 | "Indie Flower": const FontItem( 522 | name: "Indie Flower", 523 | weights: const [400], 524 | ), 525 | "Lobster": const FontItem( 526 | name: "Lobster", 527 | weights: const [400], 528 | ), 529 | "Satisfy": const FontItem( 530 | name: "Satisfy", 531 | weights: const [400], 532 | ), 533 | "Tangerine": const FontItem( 534 | name: "Tangerine", 535 | weights: const [400, 700], 536 | ), 537 | }; 538 | 539 | class FontItem { 540 | const FontItem({@required this.name, @required this.weights}); 541 | 542 | final String name; 543 | final List weights; 544 | 545 | @override 546 | String toString() => "FontItem {name: \"$name\", weights: \"$weights\"}"; 547 | } 548 | 549 | class FontSizeWidget extends StatefulWidget { 550 | const FontSizeWidget({Key key, this.size, this.onChange}) : super(key: key); 551 | 552 | final double size; 553 | 554 | final ValueChanged onChange; 555 | 556 | @override 557 | _FontSizeWidgetState createState() => new _FontSizeWidgetState(); 558 | } 559 | 560 | class _FontSizeWidgetState extends State { 561 | double _size; 562 | 563 | @override 564 | void initState() { 565 | super.initState(); 566 | _size = widget.size ?? _defaultFontSize; 567 | } 568 | 569 | void setSize(double size) { 570 | setState(() => _size = size ?? _defaultFontSize); 571 | } 572 | 573 | @override 574 | Widget build(BuildContext context) { 575 | return new Row( 576 | mainAxisAlignment: MainAxisAlignment.center, 577 | children: [ 578 | new IconButton( 579 | onPressed: () { 580 | setState(() { 581 | if (_size != 8.0) { 582 | _size--; 583 | widget.onChange(_size); 584 | } 585 | }); 586 | }, 587 | icon: new Icon( 588 | Icons.keyboard_arrow_down, 589 | ), 590 | ), 591 | new Text(_size.toInt().toString()), 592 | new IconButton( 593 | onPressed: () { 594 | setState(() { 595 | if (_size != 96.0) { 596 | _size++; 597 | widget.onChange(_size); 598 | } 599 | }); 600 | }, 601 | icon: new Icon( 602 | Icons.keyboard_arrow_up, 603 | ), 604 | ), 605 | ], 606 | ); 607 | } 608 | } 609 | -------------------------------------------------------------------------------- /rich_editor_demo/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 15 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 16 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 17 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; 18 | 9740EEBB1CF902C7004384FC /* app.flx in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB71CF902C7004384FC /* app.flx */; }; 19 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 20 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 21 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 22 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 23 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXCopyFilesBuildPhase section */ 27 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 28 | isa = PBXCopyFilesBuildPhase; 29 | buildActionMask = 2147483647; 30 | dstPath = ""; 31 | dstSubfolderSpec = 10; 32 | files = ( 33 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 34 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 35 | ); 36 | name = "Embed Frameworks"; 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXCopyFilesBuildPhase section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 43 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 44 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 45 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 46 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 47 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 50 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 51 | 9740EEB71CF902C7004384FC /* app.flx */ = {isa = PBXFileReference; lastKnownFileType = file; name = app.flx; path = Flutter/app.flx; sourceTree = ""; }; 52 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 53 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 56 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 57 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 58 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 67 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 9740EEB71CF902C7004384FC /* app.flx */, 78 | 3B80C3931E831B6300D905FE /* App.framework */, 79 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 80 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 81 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 82 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 83 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 84 | ); 85 | name = Flutter; 86 | sourceTree = ""; 87 | }; 88 | 97C146E51CF9000F007C117D = { 89 | isa = PBXGroup; 90 | children = ( 91 | 9740EEB11CF90186004384FC /* Flutter */, 92 | 97C146F01CF9000F007C117D /* Runner */, 93 | 97C146EF1CF9000F007C117D /* Products */, 94 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, 95 | ); 96 | sourceTree = ""; 97 | }; 98 | 97C146EF1CF9000F007C117D /* Products */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 97C146EE1CF9000F007C117D /* Runner.app */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | 97C146F01CF9000F007C117D /* Runner */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 110 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 111 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 112 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 113 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 114 | 97C147021CF9000F007C117D /* Info.plist */, 115 | 97C146F11CF9000F007C117D /* Supporting Files */, 116 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 117 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 118 | ); 119 | path = Runner; 120 | sourceTree = ""; 121 | }; 122 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 97C146F21CF9000F007C117D /* main.m */, 126 | ); 127 | name = "Supporting Files"; 128 | sourceTree = ""; 129 | }; 130 | /* End PBXGroup section */ 131 | 132 | /* Begin PBXNativeTarget section */ 133 | 97C146ED1CF9000F007C117D /* Runner */ = { 134 | isa = PBXNativeTarget; 135 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 136 | buildPhases = ( 137 | 9740EEB61CF901F6004384FC /* Run Script */, 138 | 97C146EA1CF9000F007C117D /* Sources */, 139 | 97C146EB1CF9000F007C117D /* Frameworks */, 140 | 97C146EC1CF9000F007C117D /* Resources */, 141 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 142 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = Runner; 149 | productName = Runner; 150 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | 97C146E61CF9000F007C117D /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 0830; 160 | ORGANIZATIONNAME = "The Chromium Authors"; 161 | TargetAttributes = { 162 | 97C146ED1CF9000F007C117D = { 163 | CreatedOnToolsVersion = 7.3.1; 164 | }; 165 | }; 166 | }; 167 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 168 | compatibilityVersion = "Xcode 3.2"; 169 | developmentRegion = English; 170 | hasScannedForEncodings = 0; 171 | knownRegions = ( 172 | en, 173 | Base, 174 | ); 175 | mainGroup = 97C146E51CF9000F007C117D; 176 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 177 | projectDirPath = ""; 178 | projectRoot = ""; 179 | targets = ( 180 | 97C146ED1CF9000F007C117D /* Runner */, 181 | ); 182 | }; 183 | /* End PBXProject section */ 184 | 185 | /* Begin PBXResourcesBuildPhase section */ 186 | 97C146EC1CF9000F007C117D /* Resources */ = { 187 | isa = PBXResourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | 9740EEBB1CF902C7004384FC /* app.flx in Resources */, 191 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 192 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, 193 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 194 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 195 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 196 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | /* End PBXResourcesBuildPhase section */ 201 | 202 | /* Begin PBXShellScriptBuildPhase section */ 203 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 204 | isa = PBXShellScriptBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | ); 208 | inputPaths = ( 209 | ); 210 | name = "Thin Binary"; 211 | outputPaths = ( 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | shellPath = /bin/sh; 215 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 216 | }; 217 | 9740EEB61CF901F6004384FC /* Run Script */ = { 218 | isa = PBXShellScriptBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | ); 222 | inputPaths = ( 223 | ); 224 | name = "Run Script"; 225 | outputPaths = ( 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | shellPath = /bin/sh; 229 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 230 | }; 231 | /* End PBXShellScriptBuildPhase section */ 232 | 233 | /* Begin PBXSourcesBuildPhase section */ 234 | 97C146EA1CF9000F007C117D /* Sources */ = { 235 | isa = PBXSourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 239 | 97C146F31CF9000F007C117D /* main.m in Sources */, 240 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXSourcesBuildPhase section */ 245 | 246 | /* Begin PBXVariantGroup section */ 247 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 248 | isa = PBXVariantGroup; 249 | children = ( 250 | 97C146FB1CF9000F007C117D /* Base */, 251 | ); 252 | name = Main.storyboard; 253 | sourceTree = ""; 254 | }; 255 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 256 | isa = PBXVariantGroup; 257 | children = ( 258 | 97C147001CF9000F007C117D /* Base */, 259 | ); 260 | name = LaunchScreen.storyboard; 261 | sourceTree = ""; 262 | }; 263 | /* End PBXVariantGroup section */ 264 | 265 | /* Begin XCBuildConfiguration section */ 266 | 97C147031CF9000F007C117D /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 269 | buildSettings = { 270 | ALWAYS_SEARCH_USER_PATHS = NO; 271 | CLANG_ANALYZER_NONNULL = YES; 272 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 273 | CLANG_CXX_LIBRARY = "libc++"; 274 | CLANG_ENABLE_MODULES = YES; 275 | CLANG_ENABLE_OBJC_ARC = YES; 276 | CLANG_WARN_BOOL_CONVERSION = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INFINITE_RECURSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 284 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 285 | CLANG_WARN_UNREACHABLE_CODE = YES; 286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 287 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 288 | COPY_PHASE_STRIP = NO; 289 | DEBUG_INFORMATION_FORMAT = dwarf; 290 | ENABLE_STRICT_OBJC_MSGSEND = YES; 291 | ENABLE_TESTABILITY = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu99; 293 | GCC_DYNAMIC_NO_PIC = NO; 294 | GCC_NO_COMMON_BLOCKS = YES; 295 | GCC_OPTIMIZATION_LEVEL = 0; 296 | GCC_PREPROCESSOR_DEFINITIONS = ( 297 | "DEBUG=1", 298 | "$(inherited)", 299 | ); 300 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 301 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 302 | GCC_WARN_UNDECLARED_SELECTOR = YES; 303 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 304 | GCC_WARN_UNUSED_FUNCTION = YES; 305 | GCC_WARN_UNUSED_VARIABLE = YES; 306 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 307 | MTL_ENABLE_DEBUG_INFO = YES; 308 | ONLY_ACTIVE_ARCH = YES; 309 | SDKROOT = iphoneos; 310 | TARGETED_DEVICE_FAMILY = "1,2"; 311 | }; 312 | name = Debug; 313 | }; 314 | 97C147041CF9000F007C117D /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BOOL_CONVERSION = YES; 325 | CLANG_WARN_CONSTANT_CONVERSION = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INFINITE_RECURSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 333 | CLANG_WARN_UNREACHABLE_CODE = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 336 | COPY_PHASE_STRIP = NO; 337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 338 | ENABLE_NS_ASSERTIONS = NO; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | GCC_C_LANGUAGE_STANDARD = gnu99; 341 | GCC_NO_COMMON_BLOCKS = YES; 342 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 343 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 344 | GCC_WARN_UNDECLARED_SELECTOR = YES; 345 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 346 | GCC_WARN_UNUSED_FUNCTION = YES; 347 | GCC_WARN_UNUSED_VARIABLE = YES; 348 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 349 | MTL_ENABLE_DEBUG_INFO = NO; 350 | SDKROOT = iphoneos; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | VALIDATE_PRODUCT = YES; 353 | }; 354 | name = Release; 355 | }; 356 | 97C147061CF9000F007C117D /* Debug */ = { 357 | isa = XCBuildConfiguration; 358 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 359 | buildSettings = { 360 | ARCHS = arm64; 361 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 362 | ENABLE_BITCODE = NO; 363 | FRAMEWORK_SEARCH_PATHS = ( 364 | "$(inherited)", 365 | "$(PROJECT_DIR)/Flutter", 366 | ); 367 | INFOPLIST_FILE = Runner/Info.plist; 368 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 369 | LIBRARY_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | PRODUCT_BUNDLE_IDENTIFIER = eu.long1.richEditorDemo; 374 | PRODUCT_NAME = "$(TARGET_NAME)"; 375 | }; 376 | name = Debug; 377 | }; 378 | 97C147071CF9000F007C117D /* Release */ = { 379 | isa = XCBuildConfiguration; 380 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 381 | buildSettings = { 382 | ARCHS = arm64; 383 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 384 | ENABLE_BITCODE = NO; 385 | FRAMEWORK_SEARCH_PATHS = ( 386 | "$(inherited)", 387 | "$(PROJECT_DIR)/Flutter", 388 | ); 389 | INFOPLIST_FILE = Runner/Info.plist; 390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 391 | LIBRARY_SEARCH_PATHS = ( 392 | "$(inherited)", 393 | "$(PROJECT_DIR)/Flutter", 394 | ); 395 | PRODUCT_BUNDLE_IDENTIFIER = eu.long1.richEditorDemo; 396 | PRODUCT_NAME = "$(TARGET_NAME)"; 397 | }; 398 | name = Release; 399 | }; 400 | /* End XCBuildConfiguration section */ 401 | 402 | /* Begin XCConfigurationList section */ 403 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 404 | isa = XCConfigurationList; 405 | buildConfigurations = ( 406 | 97C147031CF9000F007C117D /* Debug */, 407 | 97C147041CF9000F007C117D /* Release */, 408 | ); 409 | defaultConfigurationIsVisible = 0; 410 | defaultConfigurationName = Release; 411 | }; 412 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 413 | isa = XCConfigurationList; 414 | buildConfigurations = ( 415 | 97C147061CF9000F007C117D /* Debug */, 416 | 97C147071CF9000F007C117D /* Release */, 417 | ); 418 | defaultConfigurationIsVisible = 0; 419 | defaultConfigurationName = Release; 420 | }; 421 | /* End XCConfigurationList section */ 422 | }; 423 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 424 | } 425 | --------------------------------------------------------------------------------