|
|
@ -9,6 +9,7 @@ class TextEditor extends StatefulWidget { |
|
|
|
final String tabId; |
|
|
|
final String tabId; |
|
|
|
final String? initialContent; |
|
|
|
final String? initialContent; |
|
|
|
final String? fileName; |
|
|
|
final String? fileName; |
|
|
|
|
|
|
|
final String title; |
|
|
|
final Function(String, String?)? onContentChanged; |
|
|
|
final Function(String, String?)? onContentChanged; |
|
|
|
final Function(String)? onFileLoaded; |
|
|
|
final Function(String)? onFileLoaded; |
|
|
|
|
|
|
|
|
|
|
@ -17,29 +18,38 @@ class TextEditor extends StatefulWidget { |
|
|
|
required this.tabId, |
|
|
|
required this.tabId, |
|
|
|
this.initialContent, |
|
|
|
this.initialContent, |
|
|
|
this.fileName, |
|
|
|
this.fileName, |
|
|
|
|
|
|
|
this.title = '未命名', |
|
|
|
this.onContentChanged, |
|
|
|
this.onContentChanged, |
|
|
|
this.onFileLoaded, |
|
|
|
this.onFileLoaded, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@override |
|
|
|
State<TextEditor> createState() => _TextEditorState(); |
|
|
|
State<TextEditor> createState() => TextEditorState(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class _TextEditorState extends State<TextEditor> { |
|
|
|
class TextEditorState extends State<TextEditor> { |
|
|
|
late TextEditingController _controller; |
|
|
|
late TextEditingController _controller; |
|
|
|
late FocusNode _focusNode; |
|
|
|
|
|
|
|
late ScrollController _scrollController; |
|
|
|
late ScrollController _scrollController; |
|
|
|
bool _isLoading = false; |
|
|
|
bool _isLoading = false; |
|
|
|
static const int maxFileSize = 1024 * 1024; // 1MB |
|
|
|
static const int maxFileSize = 1024 * 1024; // 1MB |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool get hasFocus => _focusNode.hasFocus; |
|
|
|
|
|
|
|
FocusNode get _focusNode => focusNode; // 将原来的_focusNode改为focusNode |
|
|
|
|
|
|
|
late FocusNode focusNode = FocusNode(); // 修改声明方式 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void loadFile(BuildContext context, String filePath) async { |
|
|
|
|
|
|
|
await _loadFile(context, filePath); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
@override |
|
|
|
void initState() { |
|
|
|
void initState() { |
|
|
|
super.initState(); |
|
|
|
super.initState(); |
|
|
|
_controller = TextEditingController(text: widget.initialContent ?? ''); |
|
|
|
_controller = TextEditingController(text: widget.initialContent ?? ''); |
|
|
|
_focusNode = FocusNode(); |
|
|
|
focusNode = FocusNode(); |
|
|
|
_scrollController = ScrollController(); |
|
|
|
_scrollController = ScrollController(); |
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) { |
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) { |
|
|
|
FocusScope.of(context).requestFocus(_focusNode); |
|
|
|
FocusScope.of(context).requestFocus(focusNode); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -67,14 +77,19 @@ class _TextEditorState extends State<TextEditor> { |
|
|
|
height: 40, |
|
|
|
height: 40, |
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 16), |
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 16), |
|
|
|
decoration: BoxDecoration( |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: Colors.grey[100], |
|
|
|
color: hasFocus ? Colors.blue[50] : Colors.grey[100], |
|
|
|
border: Border(bottom: BorderSide(color: Colors.grey[300]!)), |
|
|
|
border: Border( |
|
|
|
|
|
|
|
bottom: BorderSide( |
|
|
|
|
|
|
|
color: hasFocus ? Colors.blue : Colors.grey[300]!, |
|
|
|
|
|
|
|
width: hasFocus ? 2.0 : 1.0, |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
child: Row( |
|
|
|
child: Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
children: [ |
|
|
|
Text( |
|
|
|
Text( |
|
|
|
'源文本${_controller.text.isEmpty ? '' : ' (${widget.fileName ?? ''}${_controller.text.length}字符)'}', |
|
|
|
'${widget.title}${_controller.text.isEmpty ? '' : ' (${widget.fileName ?? ''}${_controller.text.length}字符)'}', |
|
|
|
style: const TextStyle(fontWeight: FontWeight.bold), |
|
|
|
style: const TextStyle(fontWeight: FontWeight.bold), |
|
|
|
), |
|
|
|
), |
|
|
|
Row( |
|
|
|
Row( |
|
|
@ -135,10 +150,10 @@ class _TextEditorState extends State<TextEditor> { |
|
|
|
border: InputBorder.none, |
|
|
|
border: InputBorder.none, |
|
|
|
contentPadding: EdgeInsets.all(16), |
|
|
|
contentPadding: EdgeInsets.all(16), |
|
|
|
), |
|
|
|
), |
|
|
|
style: TextStyle( |
|
|
|
style: const TextStyle( |
|
|
|
fontFamily: 'monospace', |
|
|
|
fontFamily: 'Courier New', |
|
|
|
fontSize: 14, |
|
|
|
fontSize: 16, |
|
|
|
color: Theme.of(context).textTheme.bodyLarge?.color, |
|
|
|
color: Colors.black, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
], |
|
|
@ -153,7 +168,7 @@ class _TextEditorState extends State<TextEditor> { |
|
|
|
Future<void> _openFile(BuildContext context) async { |
|
|
|
Future<void> _openFile(BuildContext context) async { |
|
|
|
final result = await FilePicker.platform.pickFiles(type: FileType.any, allowMultiple: false); |
|
|
|
final result = await FilePicker.platform.pickFiles(type: FileType.any, allowMultiple: false); |
|
|
|
if (result != null && result.files.single.path != null) { |
|
|
|
if (result != null && result.files.single.path != null) { |
|
|
|
await loadFile(context, result.files.single.path!); |
|
|
|
await _loadFile(context, result.files.single.path!); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -218,7 +233,7 @@ class _TextEditorState extends State<TextEditor> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Future<void> loadFile(BuildContext context, String filePath) async { |
|
|
|
Future<void> _loadFile(BuildContext context, String filePath) async { |
|
|
|
try { |
|
|
|
try { |
|
|
|
setState(() => _isLoading = true); |
|
|
|
setState(() => _isLoading = true); |
|
|
|
final file = File(filePath); |
|
|
|
final file = File(filePath); |
|
|
|