subwrite/lib/view.dart

184 lines
5.7 KiB
Dart

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<View> 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<LineFile>(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<LineInfo>(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<void> 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<void> 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))),
]);
}
}
}