import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:subwrite/sourcelink.dart'; import 'package:subwrite/theme.dart'; import 'file.dart'; import 'ghostsense.dart'; import 'highlighter.dart'; import 'line_info.dart'; class View extends StatefulWidget { View({Key? key}) : super(key: key); @override _View createState() => _View(); } class _View extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; @override void initState() { super.initState(); _animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 300)); _animationController.repeat(reverse: true); } @override void dispose() { _animationController.dispose(); super.dispose(); } double x = 0.0; double y = 0.0; @override Widget build(BuildContext context) { LineFile doc = Provider.of(context); if (doc.backingLines.isEmpty) { return Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ ElevatedButton.icon( onPressed: () async { newFile(doc); }, icon: Icon(Icons.create, color: foreground), label: const Text("New Document"), style: ButtonStyle(backgroundColor: MaterialStateProperty.all(tabs), foregroundColor: MaterialStateProperty.all(foreground))), SizedBox( width: 20, ), ElevatedButton.icon( onPressed: () async { openFile(doc); }, icon: Icon(Icons.file_open, color: foreground), label: const Text("Open Document"), style: ButtonStyle(backgroundColor: MaterialStateProperty.all(tabs), foregroundColor: MaterialStateProperty.all(foreground))), ], ), ); } else { return MouseRegion( onHover: (PointerEvent details) { if (!doc.showSuggestions) { setState(() { x = details.localPosition.dx; y = details.localPosition.dy; }); } }, child: Stack( fit: StackFit.expand, children: [ ScrollablePositionedList.builder( physics: const AlwaysScrollableScrollPhysics(), itemScrollController: doc.scrollController, itemCount: doc.lines(), padding: EdgeInsets.zero, minCacheExtent: 10, itemBuilder: (BuildContext bcontext, int index) { return ChangeNotifierProvider.value( value: doc.backingLines[index], builder: (lcontext, lineinfo) { return Provider.of(lcontext).build(context, _animationController); }); }), Visibility( visible: doc.showSuggestions, maintainInteractivity: false, maintainState: false, child: Positioned( width: 400, height: 200, left: x, top: y, child: Container(width: 300, decoration: BoxDecoration(color: sidebarAlt, border: Border.all(color: sidebar, width: 1.0)), child: Ghostsense(doc.suggestions))), ) ], )); } } } Future openFile(LineFile doc) async { FilePickerResult? result = await FilePicker.platform.pickFiles( allowMultiple: false, type: FileType.custom, allowedExtensions: ['md', 'txt'], ); if (result != null) { doc.openFile(result.files.single.path!, 0); } else { // User canceled the picker } } Future newFile(LineFile doc) async { String? result = await FilePicker.platform.saveFile(); if (result != null) { File(result).createSync(recursive: false); doc.openFile(result, 0); doc.focus.requestFocus(); } else { // User canceled the picker } } class Suggestion { String classType; String name; Function(BuildContext context, Suggestion suggestion) callback; SourceLink link; Suggestion( this.classType, this.name, this.callback, this.link, ); @override bool operator ==(Object other) { return other is Suggestion && hashCode == other.hashCode; } @override int get hashCode => classType.hashCode + name.hashCode; Widget getWidget() { var color = function; switch (classType) { case "spelling": color = header; break; case "addto": color = keyword; break; } if (classType == "spelling") { return Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ RichText(text: TextSpan(text: name, style: TextStyle(color: color, fontFamily: "Iosevka", fontSize: 11.0, fontWeight: FontWeight.bold))), RichText(text: TextSpan(text: "dictionary", style: TextStyle(color: foreground, fontFamily: "Iosevka", fontSize: 10.0))), ]); } else { return Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ RichText(text: TextSpan(text: name, style: TextStyle(color: color, fontFamily: "Iosevka", fontSize: 11.0, fontWeight: FontWeight.bold))), RichText(text: TextSpan(text: "Add to Custom Dictionary", style: TextStyle(color: foreground, fontFamily: "Iosevka", fontSize: 10.0))), ]); } } }