mirror of
https://github.com/chenasraf/mudblock.git
synced 2026-05-18 01:48:57 +00:00
feat: terminal type, alias/trigger updates
fix: fonts/colors
This commit is contained in:
BIN
assets/fonts/FiraCode-Bold.ttf
Executable file
BIN
assets/fonts/FiraCode-Bold.ttf
Executable file
Binary file not shown.
BIN
assets/fonts/FiraCode-Light.ttf
Executable file
BIN
assets/fonts/FiraCode-Light.ttf
Executable file
Binary file not shown.
BIN
assets/fonts/FiraCode-Medium.ttf
Executable file
BIN
assets/fonts/FiraCode-Medium.ttf
Executable file
Binary file not shown.
BIN
assets/fonts/FiraCode-Regular.ttf
Executable file
BIN
assets/fonts/FiraCode-Regular.ttf
Executable file
Binary file not shown.
BIN
assets/fonts/FiraCode-Retina.ttf
Executable file
BIN
assets/fonts/FiraCode-Retina.ttf
Executable file
Binary file not shown.
BIN
assets/fonts/FiraCode-SemiBold.ttf
Executable file
BIN
assets/fonts/FiraCode-SemiBold.ttf
Executable file
Binary file not shown.
@@ -16,6 +16,10 @@ class ColorUtils {
|
||||
result.add(ColoredText.fromToken(token));
|
||||
}
|
||||
|
||||
if (line.contains('Test')) {
|
||||
debugPrint('split: $result');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (e, stack) {
|
||||
debugPrint('Error at line: $line');
|
||||
@@ -45,7 +49,8 @@ class ColorUtils {
|
||||
return hslLight.toColor();
|
||||
}
|
||||
|
||||
static Brightness getBrightness(Color color) => ThemeData.estimateBrightnessForColor(color);
|
||||
static Brightness getBrightness(Color color) =>
|
||||
ThemeData.estimateBrightnessForColor(color);
|
||||
|
||||
static bool isDark(Color color) => getBrightness(color) == Brightness.dark;
|
||||
static bool isLight(Color color) => getBrightness(color) == Brightness.light;
|
||||
@@ -89,34 +94,34 @@ const ansiFgColorMap = {
|
||||
// color: black
|
||||
30: 0xFF000000,
|
||||
// color: red
|
||||
31: 0xFF800000,
|
||||
31: 0xFFCD0000,
|
||||
// color: green
|
||||
32: 0xFF008000,
|
||||
32: 0xFF00CD00,
|
||||
// color: yellow
|
||||
33: 0xFF808000,
|
||||
33: 0xFFCDCD00,
|
||||
// color: blue
|
||||
34: 0xFF000080,
|
||||
34: 0xFF0000EE,
|
||||
// color: magenta
|
||||
35: 0xFF800080,
|
||||
35: 0xFFCD00CD,
|
||||
// color: cyan
|
||||
36: 0xFF008080,
|
||||
// color: light gray
|
||||
37: 0xFFC0C0C0,
|
||||
// color: bright black
|
||||
90: 0xFF808080,
|
||||
// color: light red
|
||||
91: 0xFFFF0000,
|
||||
// color: light green
|
||||
92: 0xFF00FF00,
|
||||
// color: light yellow
|
||||
93: 0xFFFFFF00,
|
||||
// color: light blue
|
||||
94: 0xFF0000FF,
|
||||
// color: light magenta
|
||||
95: 0xFFFF00FF,
|
||||
// color: light cyan
|
||||
96: 0xFF00FFFF,
|
||||
36: 0xFF00CDCD,
|
||||
// color: white
|
||||
37: 0xFFE5E5E5,
|
||||
// color: bright black
|
||||
90: 0xFF7F7F7F,
|
||||
// color: bright red
|
||||
91: 0xFFFF0000,
|
||||
// color: bright green
|
||||
92: 0xFF00FF00,
|
||||
// color: bright yellow
|
||||
93: 0xFFFFFF00,
|
||||
// color: bright blue
|
||||
94: 0xFF5C5CFF,
|
||||
// color: bright magenta
|
||||
95: 0xFFFF00FF,
|
||||
// color: bright cyan
|
||||
96: 0xFF00FFFF,
|
||||
// color: bright white
|
||||
97: 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
@@ -139,10 +144,6 @@ const ansiBgColorMap = {
|
||||
107: 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
const bold = 1;
|
||||
const italic = 3;
|
||||
const underline = 4;
|
||||
|
||||
/// map of xterm 256 colors to flutter color ints
|
||||
const xtermColorMap = {
|
||||
0: 0xFF000000,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../color_utils.dart';
|
||||
import '../store.dart';
|
||||
import '../string_utils.dart';
|
||||
import 'action.dart';
|
||||
import 'automation.dart';
|
||||
@@ -36,6 +38,27 @@ class Alias extends Automation {
|
||||
group: '',
|
||||
);
|
||||
|
||||
static AliasProcessResult processLine(
|
||||
GameStore store, List<Alias> triggers, String line) {
|
||||
bool showLine = true;
|
||||
final str = ColorUtils.stripColor(line);
|
||||
for (final trigger in triggers) {
|
||||
if (!trigger.isAvailable) {
|
||||
continue;
|
||||
}
|
||||
if (trigger.matches(str)) {
|
||||
trigger.invokeEffect(store, str);
|
||||
if (trigger.isRemovedFromBuffer) {
|
||||
showLine = false;
|
||||
}
|
||||
if (trigger.autoDisable) {
|
||||
trigger.tempDisabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return AliasProcessResult(lineRemoved: !showLine);
|
||||
}
|
||||
|
||||
factory Alias.fromJson(Map<String, dynamic> json) => Alias(
|
||||
id: json['id'],
|
||||
label: json['label'] ?? '',
|
||||
@@ -79,6 +102,12 @@ class Alias extends Automation {
|
||||
);
|
||||
}
|
||||
|
||||
class AliasProcessResult {
|
||||
bool lineRemoved;
|
||||
|
||||
AliasProcessResult({this.lineRemoved = false});
|
||||
}
|
||||
|
||||
String _key(String str) => 'builtin-alias-$str';
|
||||
|
||||
final builtInAliases = <Alias>[
|
||||
|
||||
@@ -6,45 +6,58 @@ import '../store.dart';
|
||||
class KeyboardIntent extends Intent {
|
||||
const KeyboardIntent(this.key);
|
||||
|
||||
final NumpadKey key;
|
||||
final LogicalKeyboardKey key;
|
||||
}
|
||||
|
||||
class KeyboardAction extends ContextAction<KeyboardIntent> with GameStoreMixin {
|
||||
@override
|
||||
void invoke(covariant KeyboardIntent intent, [BuildContext? context]) {
|
||||
if (context == null) return;
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
final store = storeOf(context);
|
||||
store.onShortcut(intent.key, context);
|
||||
if (store.currentProfile.keyboardShortcuts.get(intent.key).isNotEmpty) {
|
||||
store.onShortcut(intent.key, context);
|
||||
} else {
|
||||
store.selectInput();
|
||||
store.setInput(
|
||||
store.input.text + intent.key.keyLabel.replaceAll('Numpad ', ''),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool isEnabled(KeyboardIntent intent, [BuildContext? context]) {
|
||||
if (context == null) return false;
|
||||
if (context == null) {
|
||||
return false;
|
||||
}
|
||||
final store = storeOf(context);
|
||||
if (store.currentProfile.keyboardShortcuts.get(intent.key).isEmpty) return false;
|
||||
if (store.currentProfile.keyboardShortcuts.get(intent.key).isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return super.isEnabled(intent, context);
|
||||
}
|
||||
}
|
||||
|
||||
enum NumpadKey {
|
||||
numpad0,
|
||||
numpad1,
|
||||
numpad2,
|
||||
numpad3,
|
||||
numpad4,
|
||||
numpad5,
|
||||
numpad6,
|
||||
numpad7,
|
||||
numpad8,
|
||||
numpad9,
|
||||
numpadEnter,
|
||||
numpadDecimal,
|
||||
numpadAdd,
|
||||
numpadSubtract,
|
||||
numpadMultiply,
|
||||
numpadDivide,
|
||||
numpadEqual,
|
||||
}
|
||||
const numpadKeys = [
|
||||
LogicalKeyboardKey.numpad0,
|
||||
LogicalKeyboardKey.numpad1,
|
||||
LogicalKeyboardKey.numpad2,
|
||||
LogicalKeyboardKey.numpad3,
|
||||
LogicalKeyboardKey.numpad4,
|
||||
LogicalKeyboardKey.numpad5,
|
||||
LogicalKeyboardKey.numpad6,
|
||||
LogicalKeyboardKey.numpad7,
|
||||
LogicalKeyboardKey.numpad8,
|
||||
LogicalKeyboardKey.numpad9,
|
||||
LogicalKeyboardKey.numpadEnter,
|
||||
LogicalKeyboardKey.numpadDecimal,
|
||||
LogicalKeyboardKey.numpadAdd,
|
||||
LogicalKeyboardKey.numpadSubtract,
|
||||
LogicalKeyboardKey.numpadMultiply,
|
||||
LogicalKeyboardKey.numpadDivide,
|
||||
LogicalKeyboardKey.numpadEqual,
|
||||
];
|
||||
|
||||
class KeyboardShortcuts {
|
||||
String numpad0;
|
||||
@@ -105,25 +118,25 @@ class KeyboardShortcuts {
|
||||
numpadEqual: '',
|
||||
);
|
||||
|
||||
String get(NumpadKey key) =>
|
||||
String get(LogicalKeyboardKey key) =>
|
||||
{
|
||||
NumpadKey.numpad0: numpad0,
|
||||
NumpadKey.numpad1: numpad1,
|
||||
NumpadKey.numpad2: numpad2,
|
||||
NumpadKey.numpad3: numpad3,
|
||||
NumpadKey.numpad4: numpad4,
|
||||
NumpadKey.numpad5: numpad5,
|
||||
NumpadKey.numpad6: numpad6,
|
||||
NumpadKey.numpad7: numpad7,
|
||||
NumpadKey.numpad8: numpad8,
|
||||
NumpadKey.numpad9: numpad9,
|
||||
NumpadKey.numpadEnter: numpadEnter,
|
||||
NumpadKey.numpadDecimal: numpadDecimal,
|
||||
NumpadKey.numpadAdd: numpadAdd,
|
||||
NumpadKey.numpadSubtract: numpadSubtract,
|
||||
NumpadKey.numpadMultiply: numpadMultiply,
|
||||
NumpadKey.numpadDivide: numpadDivide,
|
||||
NumpadKey.numpadEqual: numpadEqual,
|
||||
LogicalKeyboardKey.numpad0: numpad0,
|
||||
LogicalKeyboardKey.numpad1: numpad1,
|
||||
LogicalKeyboardKey.numpad2: numpad2,
|
||||
LogicalKeyboardKey.numpad3: numpad3,
|
||||
LogicalKeyboardKey.numpad4: numpad4,
|
||||
LogicalKeyboardKey.numpad5: numpad5,
|
||||
LogicalKeyboardKey.numpad6: numpad6,
|
||||
LogicalKeyboardKey.numpad7: numpad7,
|
||||
LogicalKeyboardKey.numpad8: numpad8,
|
||||
LogicalKeyboardKey.numpad9: numpad9,
|
||||
LogicalKeyboardKey.numpadEnter: numpadEnter,
|
||||
LogicalKeyboardKey.numpadDecimal: numpadDecimal,
|
||||
LogicalKeyboardKey.numpadAdd: numpadAdd,
|
||||
LogicalKeyboardKey.numpadSubtract: numpadSubtract,
|
||||
LogicalKeyboardKey.numpadMultiply: numpadMultiply,
|
||||
LogicalKeyboardKey.numpadDivide: numpadDivide,
|
||||
LogicalKeyboardKey.numpadEqual: numpadEqual,
|
||||
}[key] ??
|
||||
'';
|
||||
|
||||
@@ -210,79 +223,80 @@ class KeyboardShortcuts {
|
||||
numpadEqual: numpadEqual ?? this.numpadEqual,
|
||||
);
|
||||
|
||||
KeyboardShortcuts copyWithMap(Map<NumpadKey, String> map) => copyWith(
|
||||
numpad0: map[NumpadKey.numpad0],
|
||||
numpad1: map[NumpadKey.numpad1],
|
||||
numpad2: map[NumpadKey.numpad2],
|
||||
numpad3: map[NumpadKey.numpad3],
|
||||
numpad4: map[NumpadKey.numpad4],
|
||||
numpad5: map[NumpadKey.numpad5],
|
||||
numpad6: map[NumpadKey.numpad6],
|
||||
numpad7: map[NumpadKey.numpad7],
|
||||
numpad8: map[NumpadKey.numpad8],
|
||||
numpad9: map[NumpadKey.numpad9],
|
||||
numpadEnter: map[NumpadKey.numpadEnter],
|
||||
numpadDecimal: map[NumpadKey.numpadDecimal],
|
||||
numpadAdd: map[NumpadKey.numpadAdd],
|
||||
numpadSubtract: map[NumpadKey.numpadSubtract],
|
||||
numpadMultiply: map[NumpadKey.numpadMultiply],
|
||||
numpadDivide: map[NumpadKey.numpadDivide],
|
||||
numpadEqual: map[NumpadKey.numpadEqual],
|
||||
KeyboardShortcuts copyWithMap(Map<LogicalKeyboardKey, String> map) =>
|
||||
copyWith(
|
||||
numpad0: map[LogicalKeyboardKey.numpad0],
|
||||
numpad1: map[LogicalKeyboardKey.numpad1],
|
||||
numpad2: map[LogicalKeyboardKey.numpad2],
|
||||
numpad3: map[LogicalKeyboardKey.numpad3],
|
||||
numpad4: map[LogicalKeyboardKey.numpad4],
|
||||
numpad5: map[LogicalKeyboardKey.numpad5],
|
||||
numpad6: map[LogicalKeyboardKey.numpad6],
|
||||
numpad7: map[LogicalKeyboardKey.numpad7],
|
||||
numpad8: map[LogicalKeyboardKey.numpad8],
|
||||
numpad9: map[LogicalKeyboardKey.numpad9],
|
||||
numpadEnter: map[LogicalKeyboardKey.numpadEnter],
|
||||
numpadDecimal: map[LogicalKeyboardKey.numpadDecimal],
|
||||
numpadAdd: map[LogicalKeyboardKey.numpadAdd],
|
||||
numpadSubtract: map[LogicalKeyboardKey.numpadSubtract],
|
||||
numpadMultiply: map[LogicalKeyboardKey.numpadMultiply],
|
||||
numpadDivide: map[LogicalKeyboardKey.numpadDivide],
|
||||
numpadEqual: map[LogicalKeyboardKey.numpadEqual],
|
||||
);
|
||||
}
|
||||
|
||||
const numpadKeysIntentMap = <ShortcutActivator, Intent>{
|
||||
SingleActivator(LogicalKeyboardKey.numpad0):
|
||||
KeyboardIntent(NumpadKey.numpad0),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad0),
|
||||
SingleActivator(LogicalKeyboardKey.numpad1):
|
||||
KeyboardIntent(NumpadKey.numpad1),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad1),
|
||||
SingleActivator(LogicalKeyboardKey.numpad2):
|
||||
KeyboardIntent(NumpadKey.numpad2),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad2),
|
||||
SingleActivator(LogicalKeyboardKey.numpad3):
|
||||
KeyboardIntent(NumpadKey.numpad3),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad3),
|
||||
SingleActivator(LogicalKeyboardKey.numpad4):
|
||||
KeyboardIntent(NumpadKey.numpad4),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad4),
|
||||
SingleActivator(LogicalKeyboardKey.numpad5):
|
||||
KeyboardIntent(NumpadKey.numpad5),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad5),
|
||||
SingleActivator(LogicalKeyboardKey.numpad6):
|
||||
KeyboardIntent(NumpadKey.numpad6),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad6),
|
||||
SingleActivator(LogicalKeyboardKey.numpad7):
|
||||
KeyboardIntent(NumpadKey.numpad7),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad7),
|
||||
SingleActivator(LogicalKeyboardKey.numpad8):
|
||||
KeyboardIntent(NumpadKey.numpad8),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad8),
|
||||
SingleActivator(LogicalKeyboardKey.numpad9):
|
||||
KeyboardIntent(NumpadKey.numpad9),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpad9),
|
||||
SingleActivator(LogicalKeyboardKey.numpadDivide):
|
||||
KeyboardIntent(NumpadKey.numpadDivide),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpadDivide),
|
||||
SingleActivator(LogicalKeyboardKey.numpadMultiply):
|
||||
KeyboardIntent(NumpadKey.numpadMultiply),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpadMultiply),
|
||||
SingleActivator(LogicalKeyboardKey.numpadSubtract):
|
||||
KeyboardIntent(NumpadKey.numpadSubtract),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpadSubtract),
|
||||
SingleActivator(LogicalKeyboardKey.numpadAdd):
|
||||
KeyboardIntent(NumpadKey.numpadAdd),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpadAdd),
|
||||
SingleActivator(LogicalKeyboardKey.numpadDecimal):
|
||||
KeyboardIntent(NumpadKey.numpadDecimal),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpadDecimal),
|
||||
SingleActivator(LogicalKeyboardKey.numpadEnter):
|
||||
KeyboardIntent(NumpadKey.numpadEnter),
|
||||
KeyboardIntent(LogicalKeyboardKey.numpadEnter),
|
||||
};
|
||||
|
||||
final numpadKeyLabels = {
|
||||
NumpadKey.numpad0: '0',
|
||||
NumpadKey.numpad1: '1',
|
||||
NumpadKey.numpad2: '2',
|
||||
NumpadKey.numpad3: '3',
|
||||
NumpadKey.numpad4: '4',
|
||||
NumpadKey.numpad5: '5',
|
||||
NumpadKey.numpad6: '6',
|
||||
NumpadKey.numpad7: '7',
|
||||
NumpadKey.numpad8: '8',
|
||||
NumpadKey.numpad9: '9',
|
||||
NumpadKey.numpadEnter: 'Enter',
|
||||
NumpadKey.numpadDecimal: '.',
|
||||
NumpadKey.numpadAdd: '+',
|
||||
NumpadKey.numpadSubtract: '-',
|
||||
NumpadKey.numpadMultiply: '*',
|
||||
NumpadKey.numpadDivide: '/',
|
||||
NumpadKey.numpadEqual: '=',
|
||||
LogicalKeyboardKey.numpad0: '0',
|
||||
LogicalKeyboardKey.numpad1: '1',
|
||||
LogicalKeyboardKey.numpad2: '2',
|
||||
LogicalKeyboardKey.numpad3: '3',
|
||||
LogicalKeyboardKey.numpad4: '4',
|
||||
LogicalKeyboardKey.numpad5: '5',
|
||||
LogicalKeyboardKey.numpad6: '6',
|
||||
LogicalKeyboardKey.numpad7: '7',
|
||||
LogicalKeyboardKey.numpad8: '8',
|
||||
LogicalKeyboardKey.numpad9: '9',
|
||||
LogicalKeyboardKey.numpadEnter: 'Enter',
|
||||
LogicalKeyboardKey.numpadDecimal: '.',
|
||||
LogicalKeyboardKey.numpadAdd: '+',
|
||||
LogicalKeyboardKey.numpadSubtract: '-',
|
||||
LogicalKeyboardKey.numpadMultiply: '*',
|
||||
LogicalKeyboardKey.numpadDivide: '/',
|
||||
LogicalKeyboardKey.numpadEqual: '=',
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../color_utils.dart';
|
||||
import '../store.dart';
|
||||
import '../string_utils.dart';
|
||||
import 'action.dart';
|
||||
import 'automation.dart';
|
||||
@@ -49,6 +51,27 @@ class Trigger extends Automation {
|
||||
group: json['group'] ?? '',
|
||||
);
|
||||
|
||||
static TriggerProcessResult processLine(
|
||||
GameStore store, List<Trigger> triggers, String line) {
|
||||
bool showLine = true;
|
||||
final str = ColorUtils.stripColor(line);
|
||||
for (final trigger in triggers) {
|
||||
if (!trigger.isAvailable) {
|
||||
continue;
|
||||
}
|
||||
if (trigger.matches(str)) {
|
||||
trigger.invokeEffect(store, str);
|
||||
if (trigger.isRemovedFromBuffer) {
|
||||
showLine = false;
|
||||
}
|
||||
if (trigger.autoDisable) {
|
||||
trigger.tempDisabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TriggerProcessResult(lineRemoved: !showLine);
|
||||
}
|
||||
|
||||
@override
|
||||
Trigger copyWith({
|
||||
String? id,
|
||||
@@ -77,3 +100,10 @@ class Trigger extends Automation {
|
||||
group: group ?? this.group,
|
||||
);
|
||||
}
|
||||
|
||||
class TriggerProcessResult {
|
||||
bool lineRemoved;
|
||||
|
||||
TriggerProcessResult({this.lineRemoved = false});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'interfaces.dart';
|
||||
import 'reader.dart';
|
||||
import '../consts.dart' as consts;
|
||||
@@ -49,6 +51,17 @@ class ColorToken {
|
||||
text == other.text &&
|
||||
fgColor == other.fgColor &&
|
||||
bgColor == other.bgColor;
|
||||
|
||||
void setStyle(int code) {
|
||||
// debugPrint('setStyle: $code');
|
||||
if (code == consts.boldByte) {
|
||||
bold = true;
|
||||
} else if (code == consts.italicByte) {
|
||||
italic = true;
|
||||
} else if (code == consts.underlineByte) {
|
||||
underline = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ColorParser implements IReader {
|
||||
@@ -85,29 +98,57 @@ class ColorParser implements IReader {
|
||||
final color = consumeUntil('m');
|
||||
reader.read();
|
||||
final colors = color.split(';');
|
||||
if (colors.length == 1) {
|
||||
final code = int.tryParse(colors[0]) ?? 0;
|
||||
if (code == consts.boldByte) {
|
||||
token.bold = true;
|
||||
} else if (code == consts.italicByte) {
|
||||
token.italic = true;
|
||||
} else if (code == consts.underlineByte) {
|
||||
token.underline = true;
|
||||
} else {
|
||||
token.fgColor = int.tryParse(colors[0]) ?? 0;
|
||||
}
|
||||
} else if (colors.length == 2) {
|
||||
token.bgColor = int.tryParse(colors[0]) ?? 1;
|
||||
token.fgColor = int.tryParse(colors[1]) ?? 0;
|
||||
} else if (colors.length == 3) {
|
||||
if (colors[0] == '38' && colors[1] == '5') {
|
||||
final first = int.tryParse(colors[0]) ?? 0;
|
||||
final second =
|
||||
colors.length > 1 ? int.tryParse(colors[1]) ?? 0 : 0;
|
||||
final third =
|
||||
colors.length > 2 ? int.tryParse(colors[2]) ?? 0 : 0;
|
||||
int fg;
|
||||
int bg;
|
||||
if (first < 30) {
|
||||
token.setStyle(first);
|
||||
fg = second;
|
||||
bg = third;
|
||||
} else {
|
||||
if (first == 38 && second == 5) {
|
||||
token.xterm256 = true;
|
||||
token.fgColor = int.tryParse(colors[2]) ?? 0;
|
||||
fg = third;
|
||||
bg = 0;
|
||||
} else {
|
||||
token.bgColor = int.tryParse(colors[0]) ?? 1;
|
||||
token.fgColor = int.tryParse(colors[1]) ?? 0;
|
||||
fg = first;
|
||||
bg = second;
|
||||
}
|
||||
}
|
||||
token.fgColor = fg;
|
||||
token.bgColor = bg;
|
||||
// if (colors.length == 1) {
|
||||
// final code = int.tryParse(colors[0]) ?? 0;
|
||||
// if (code == consts.boldByte) {
|
||||
// token.bold = true;
|
||||
// } else if (code == consts.italicByte) {
|
||||
// token.italic = true;
|
||||
// } else if (code == consts.underlineByte) {
|
||||
// token.underline = true;
|
||||
// } else {
|
||||
// token.fgColor = int.tryParse(colors[0]) ?? 0;
|
||||
// }
|
||||
// } else if (colors.length == 2) {
|
||||
// final code = int.tryParse(colors[0]) ?? 0;
|
||||
// if (code < 30) {
|
||||
// token.fgColor = int.tryParse(colors[1]) ?? 0;
|
||||
// } else {
|
||||
// token.fgColor = int.tryParse(colors[1]) ?? 0;
|
||||
// token.bgColor = int.tryParse(colors[0]) ?? 1;
|
||||
// }
|
||||
// } else if (colors.length == 3) {
|
||||
// if (colors[0] == '38' && colors[1] == '5') {
|
||||
// token.xterm256 = true;
|
||||
// token.fgColor = int.tryParse(colors[2]) ?? 0;
|
||||
// } else {
|
||||
// token.bgColor = int.tryParse(colors[0]) ?? 1;
|
||||
// token.fgColor = int.tryParse(colors[1]) ?? 0;
|
||||
// }
|
||||
// }
|
||||
token.text = consumeUntil(consts.esc);
|
||||
return token;
|
||||
}
|
||||
@@ -183,3 +224,4 @@ class ColorParser implements IReader {
|
||||
@override
|
||||
setPosition(int position) => index = position;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:ctelnet/ctelnet.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mudblock/core/features/settings.dart';
|
||||
import 'package:mudblock/core/profile_presets.dart';
|
||||
import 'package:mudblock/core/storage.dart';
|
||||
import 'package:mudblock/pages/select_profile_page.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'color_utils.dart';
|
||||
import '../core/features/settings.dart';
|
||||
import '../core/profile_presets.dart';
|
||||
import '../core/storage.dart';
|
||||
import '../core/string_utils.dart';
|
||||
import 'consts.dart';
|
||||
import 'features/action.dart';
|
||||
import 'features/alias.dart';
|
||||
import 'features/keyboard_shortcuts.dart';
|
||||
import 'features/profile.dart';
|
||||
import 'features/trigger.dart';
|
||||
import 'routes.dart';
|
||||
|
||||
const maxLines = 2000;
|
||||
@@ -46,6 +47,8 @@ class GameStore extends ChangeNotifier {
|
||||
RegExp("(?<!$commandSeparator)$commandSeparator(?!$commandSeparator)");
|
||||
|
||||
MUDProfile get currentProfile => _currentProfile!;
|
||||
List<Alias> get aliases => currentProfile.aliases;
|
||||
List<Trigger> get triggers => currentProfile.triggers;
|
||||
|
||||
get connected => _clientReady && _client.connected;
|
||||
|
||||
@@ -57,11 +60,17 @@ class GameStore extends ChangeNotifier {
|
||||
return this;
|
||||
}
|
||||
|
||||
void appStart(BuildContext context) async {
|
||||
echoSystem('''
|
||||
Welcome to MudBlock!
|
||||
To get started, tap the hamburger menu at the top right corner and
|
||||
select a profile.
|
||||
'''
|
||||
.trimMultiline());
|
||||
// For more help, type "mudhelp"
|
||||
}
|
||||
|
||||
void selectProfileAndConnect(BuildContext context) async {
|
||||
// final profile = await showDialog<MUDProfile?>(
|
||||
// context: context,
|
||||
// builder: (context) => const SelectProfilePage(),
|
||||
// );
|
||||
final profile = await Navigator.pushNamed(
|
||||
context,
|
||||
Paths.selectProfile,
|
||||
@@ -89,44 +98,6 @@ class GameStore extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool processTriggers(String line) {
|
||||
bool showLine = true;
|
||||
final str = ColorUtils.stripColor(line);
|
||||
for (final trigger in currentProfile.triggers) {
|
||||
if (!trigger.isAvailable) {
|
||||
continue;
|
||||
}
|
||||
if (trigger.matches(str)) {
|
||||
trigger.invokeEffect(this, str);
|
||||
if (trigger.isRemovedFromBuffer) {
|
||||
showLine = false;
|
||||
}
|
||||
if (trigger.autoDisable) {
|
||||
trigger.tempDisabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return showLine;
|
||||
}
|
||||
|
||||
bool processAliases(String line) {
|
||||
bool sendLine = true;
|
||||
final str = line;
|
||||
for (final alias in [...builtInAliases, ...currentProfile.aliases]) {
|
||||
if (!alias.isAvailable) {
|
||||
continue;
|
||||
}
|
||||
if (alias.matches(str)) {
|
||||
alias.invokeEffect(this, str);
|
||||
sendLine = false;
|
||||
}
|
||||
if (alias.autoDisable) {
|
||||
alias.tempDisabled = true;
|
||||
}
|
||||
}
|
||||
return sendLine;
|
||||
}
|
||||
|
||||
Future<void> _onConnect() async {
|
||||
_clientReady = true;
|
||||
echoSystem('Connected');
|
||||
@@ -158,9 +129,13 @@ class GameStore extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void onRawData(List<int> bytes) {
|
||||
debugPrint('onRawData');
|
||||
try {
|
||||
final data = Message(bytes);
|
||||
handleMCCPHandshake(data);
|
||||
handleSpecialMessages(data);
|
||||
if (data.text.isEmpty) {
|
||||
return;
|
||||
}
|
||||
for (final line in data.text.split(incomingMsgSplitPattern)) {
|
||||
onLine(line);
|
||||
}
|
||||
@@ -173,15 +148,17 @@ class GameStore extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void onData(Message data) {
|
||||
debugPrint('onData');
|
||||
try {
|
||||
if (currentProfile.mccpEnabled && isCompressed) {
|
||||
_rawStreamController.add(data.bytes);
|
||||
return;
|
||||
}
|
||||
if (currentProfile.mccpEnabled) {
|
||||
handleMCCPHandshake(data);
|
||||
debugPrint('onData: ${data.text}');
|
||||
handleSpecialMessages(data);
|
||||
if (data.text.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final line in data.text.split(incomingMsgSplitPattern)) {
|
||||
onLine(line);
|
||||
}
|
||||
@@ -192,18 +169,42 @@ class GameStore extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void handleMCCPHandshake(Message data) {
|
||||
if (isCompressed) {
|
||||
if (data.se()) {
|
||||
disableMCCP();
|
||||
}
|
||||
} else {
|
||||
if (data.sb(86)) {
|
||||
enableMCCP();
|
||||
}
|
||||
if (data.will(86)) {
|
||||
requestMCCP();
|
||||
echo('Compression requested');
|
||||
void handleSpecialMessages(Message data) {
|
||||
if (data.isCommand) {
|
||||
debugPrint('Received command: ${data.bytes}');
|
||||
}
|
||||
if (data.doo(24)) {
|
||||
debugPrint('Received terminal type WILL request');
|
||||
sendBytes([Symbols.iac, Symbols.will, 24, Symbols.iac, Symbols.se]);
|
||||
} else if (data.sb(24) && data.bytes[3] == 1) {
|
||||
debugPrint('Received terminal type SEND request');
|
||||
final bytes = [
|
||||
Symbols.iac,
|
||||
Symbols.sb,
|
||||
24,
|
||||
0,
|
||||
...const AsciiEncoder().convert('Mublock'),
|
||||
Symbols.iac,
|
||||
Symbols.se
|
||||
];
|
||||
debugPrint('Sending terminal type: $bytes');
|
||||
sendBytes(bytes);
|
||||
} else if (!currentProfile.mccpEnabled) {
|
||||
return;
|
||||
}
|
||||
// MCCP
|
||||
else {
|
||||
if (isCompressed) {
|
||||
if (data.se()) {
|
||||
disableMCCP();
|
||||
}
|
||||
} else {
|
||||
if (data.sb(86)) {
|
||||
enableMCCP();
|
||||
} else if (data.will(86)) {
|
||||
requestMCCP();
|
||||
echo('Compression requested');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,8 +227,8 @@ class GameStore extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void onLine(String line) {
|
||||
final showLine = processTriggers(line);
|
||||
if (showLine) {
|
||||
final result = Trigger.processLine(this, triggers, line);
|
||||
if (!result.lineRemoved) {
|
||||
echo(line);
|
||||
}
|
||||
}
|
||||
@@ -237,7 +238,7 @@ class GameStore extends ChangeNotifier {
|
||||
|
||||
/// echo - echo to screen, DOES NOT split by msgSplitPattern, is not send to server
|
||||
void echo(String line) {
|
||||
if (currentProfile.settings.showTimestamps) {
|
||||
if (_currentProfile != null && currentProfile.settings.showTimestamps) {
|
||||
line = '[${DateTime.now().toIso8601String()}] $line';
|
||||
}
|
||||
_lines.add(line);
|
||||
@@ -262,13 +263,13 @@ class GameStore extends ChangeNotifier {
|
||||
|
||||
/// sendBytes - raw send bytes - DOES NOT split by outgoingMsgSplitPattern, no processing
|
||||
void sendBytes(List<int> bytes) {
|
||||
var output = bytes;
|
||||
_client.sendBytes(output);
|
||||
debugPrint('Sending bytes: $bytes');
|
||||
_client.sendBytes(bytes);
|
||||
}
|
||||
|
||||
/// sendString - send string - DOES NOT split by outgoingMsgSplitPattern, no processing
|
||||
void sendString(String line) {
|
||||
debugPrint('sending string: $line');
|
||||
debugPrint('Sending string: $line');
|
||||
_client.send(line + newline);
|
||||
}
|
||||
|
||||
@@ -276,11 +277,10 @@ class GameStore extends ChangeNotifier {
|
||||
void send(String text) {
|
||||
for (var line in _splitCsp(text)) {
|
||||
if (isCompressed) {
|
||||
debugPrint(
|
||||
'sending bytes${isCompressed ? ' (compressed)' : ''}: $line');
|
||||
debugPrint('Sending compressed bytes: $line');
|
||||
sendBytes(line.codeUnits + newline.codeUnits);
|
||||
} else {
|
||||
debugPrint('sending string: $line');
|
||||
debugPrint('Sending string: $line');
|
||||
sendString(line);
|
||||
}
|
||||
}
|
||||
@@ -291,8 +291,8 @@ class GameStore extends ChangeNotifier {
|
||||
for (var line in _splitCsp(text)) {
|
||||
line = MUDAction.doVariableReplacements(this, line);
|
||||
debugPrint('processing aliases for: $line');
|
||||
var sendLine = processAliases(line);
|
||||
if (sendLine) {
|
||||
var result = Alias.processLine(this, aliases, line);
|
||||
if (!result.lineRemoved) {
|
||||
sendString(line);
|
||||
}
|
||||
}
|
||||
@@ -307,7 +307,7 @@ class GameStore extends ChangeNotifier {
|
||||
}
|
||||
|
||||
/// submitInput - echo input, process aliases and triggers, then send, scroll to end, select input
|
||||
void submitInput(String text) {
|
||||
void submitAsInput(String text) {
|
||||
if (!_clientReady || !_client.connected) {
|
||||
return;
|
||||
}
|
||||
@@ -417,10 +417,10 @@ class GameStore extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void onShortcut(NumpadKey key, BuildContext context) {
|
||||
void onShortcut(LogicalKeyboardKey key, BuildContext context) {
|
||||
final action = currentProfile.keyboardShortcuts.get(key);
|
||||
if (action.isNotEmpty) {
|
||||
submitInput(action);
|
||||
submitAsInput(action);
|
||||
selectInput();
|
||||
}
|
||||
}
|
||||
@@ -455,11 +455,15 @@ class GameStore extends ChangeNotifier {
|
||||
void onProfileUpdate() {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
static GameStore of(BuildContext context) {
|
||||
return Provider.of<GameStore>(context, listen: false);
|
||||
}
|
||||
}
|
||||
|
||||
mixin GameStoreMixin {
|
||||
GameStore storeOf(BuildContext context) =>
|
||||
Provider.of<GameStore>(context, listen: false);
|
||||
GameStore.of(context);
|
||||
}
|
||||
|
||||
mixin GameStoreStateMixin<T extends StatefulWidget> on State<T> {
|
||||
|
||||
@@ -25,7 +25,7 @@ class HomePageState extends State<HomePage>
|
||||
void initState() {
|
||||
super.initState();
|
||||
windowManager.addListener(this);
|
||||
Future.delayed(Duration.zero, () => store.selectProfileAndConnect(context));
|
||||
Future.delayed(Duration.zero, () => store.appStart(context));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -38,7 +38,7 @@ class HomePageState extends State<HomePage>
|
||||
Widget build(BuildContext context) {
|
||||
const consoleStyle = TextStyle(
|
||||
color: Colors.white,
|
||||
fontFamily: 'Menlo',
|
||||
fontFamily: 'Fira Code',
|
||||
fontSize: 16,
|
||||
height: 1,
|
||||
);
|
||||
@@ -71,46 +71,49 @@ class HomePageState extends State<HomePage>
|
||||
child: Stack(
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: store.selectInput,
|
||||
child: SingleChildScrollView(
|
||||
controller: store.scrollController,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SelectableText.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
for (final line in lines) ...[
|
||||
for (final segment
|
||||
in ColorUtils.split(line))
|
||||
TextSpan(
|
||||
text: segment.text,
|
||||
style: consoleStyle.copyWith(
|
||||
color: Color(
|
||||
segment.themedFgColor),
|
||||
backgroundColor: Color(
|
||||
segment.themedBgColor),
|
||||
fontWeight: segment.bold
|
||||
? FontWeight.w800
|
||||
: null,
|
||||
fontStyle: segment.italic
|
||||
? FontStyle.italic
|
||||
: null,
|
||||
decoration: segment.underline
|
||||
? TextDecoration.underline
|
||||
: null,
|
||||
child: Focus(
|
||||
child: SelectableText.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
for (final line in lines) ...[
|
||||
for (final segment
|
||||
in ColorUtils.split(line))
|
||||
TextSpan(
|
||||
text: segment.text,
|
||||
style: consoleStyle.copyWith(
|
||||
color: Color(
|
||||
segment.themedFgColor),
|
||||
backgroundColor: Color(
|
||||
segment.themedBgColor),
|
||||
fontWeight: segment.bold
|
||||
? FontWeight.w900
|
||||
: FontWeight.w400,
|
||||
fontStyle: segment.italic
|
||||
? FontStyle.italic
|
||||
: null,
|
||||
decoration: segment.underline
|
||||
? TextDecoration.underline
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const TextSpan(
|
||||
text: newline,
|
||||
style:
|
||||
consoleStyle, // .copyWith(fontSize: 1),
|
||||
),
|
||||
const TextSpan(
|
||||
text: newline,
|
||||
style:
|
||||
consoleStyle, // .copyWith(fontSize: 1),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
enableInteractiveSelection: true,
|
||||
selectionWidthStyle: BoxWidthStyle.tight,
|
||||
selectionHeightStyle: BoxHeightStyle.max,
|
||||
),
|
||||
enableInteractiveSelection: true,
|
||||
selectionWidthStyle: BoxWidthStyle.tight,
|
||||
selectionHeightStyle: BoxHeightStyle.max,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -136,7 +139,7 @@ class HomePageState extends State<HomePage>
|
||||
autofocus: true,
|
||||
focusNode: store.inputFocus,
|
||||
controller: store.input,
|
||||
onSubmitted: store.submitInput,
|
||||
onSubmitted: store.submitAsInput,
|
||||
onTap: store.selectInput,
|
||||
style: consoleStyle.copyWith(color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
|
||||
@@ -32,27 +32,27 @@ class HomeScaffold extends StatelessWidget with GameStoreMixin {
|
||||
builder: builder,
|
||||
),
|
||||
endDrawer: Drawer(
|
||||
child: ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Image(
|
||||
image: AssetImage('assets/images/logo/logo.png'),
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: GameStore.consumer(
|
||||
builder: (context, store, child) => ListView(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Image(
|
||||
image: AssetImage('assets/images/logo/logo.png'),
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
title: const Text('Mudblock'),
|
||||
subtitle: FutureBuilder<String>(
|
||||
future: PackageInfo.fromPlatform().then((pkg) => pkg.version),
|
||||
builder: (context, data) {
|
||||
final version = data.data ?? '...';
|
||||
return Text('v$version');
|
||||
},
|
||||
),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.about),
|
||||
),
|
||||
title: const Text('Mudblock'),
|
||||
subtitle: FutureBuilder<String>(
|
||||
future: PackageInfo.fromPlatform().then((pkg) => pkg.version),
|
||||
builder: (context, data) {
|
||||
final version = data.data ?? '...';
|
||||
return Text('v$version');
|
||||
},
|
||||
),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.about),
|
||||
),
|
||||
const Divider(),
|
||||
GameStore.consumer(
|
||||
builder: (context, store, child) => ListTile(
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: store.connected
|
||||
? Text(store.currentProfile.name)
|
||||
: const Text('Not connected'),
|
||||
@@ -78,58 +78,53 @@ class HomeScaffold extends StatelessWidget with GameStoreMixin {
|
||||
store.selectProfileAndConnect(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Button Sets'),
|
||||
leading: const Icon(GameButtonSetData.iconData),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.buttons),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Aliases'),
|
||||
leading: const Icon(Alias.iconData),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.aliases),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Triggers'),
|
||||
leading: const Icon(Trigger.iconData),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.triggers),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Variables'),
|
||||
leading: const Icon(Variable.iconData),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.variables),
|
||||
),
|
||||
if (PlatformUtils.isDesktop)
|
||||
ListTile(
|
||||
title: const Text('Keyboard Shortcuts'),
|
||||
leading: const Icon(Icons.keyboard),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.shortcuts),
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: const Text('Settings'),
|
||||
leading: const Icon(Icons.settings),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.settings),
|
||||
),
|
||||
GameStore.consumer(
|
||||
builder: (context, store, child) {
|
||||
if (store.connected) {
|
||||
if (store.connected) ...[
|
||||
ListTile(
|
||||
title: const Text('Button Sets'),
|
||||
leading: const Icon(GameButtonSetData.iconData),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.buttons),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Aliases'),
|
||||
leading: const Icon(Alias.iconData),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.aliases),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Triggers'),
|
||||
leading: const Icon(Trigger.iconData),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.triggers),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Variables'),
|
||||
leading: const Icon(Variable.iconData),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.variables),
|
||||
),
|
||||
if (PlatformUtils.isDesktop)
|
||||
ListTile(
|
||||
title: const Text('Disconnect'),
|
||||
leading: const Icon(Icons.exit_to_app),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
await gameStore.disconnect();
|
||||
if (context.mounted) {
|
||||
gameStore.selectProfileAndConnect(context);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
],
|
||||
title: const Text('Keyboard Shortcuts'),
|
||||
leading: const Icon(Icons.keyboard),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.shortcuts),
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: const Text('Settings'),
|
||||
leading: const Icon(Icons.settings),
|
||||
onTap: () => Navigator.pushNamed(context, Paths.settings),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Disconnect'),
|
||||
leading: const Icon(Icons.exit_to_app),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
await gameStore.disconnect();
|
||||
if (context.mounted) {
|
||||
gameStore.selectProfileAndConnect(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../core/features/keyboard_shortcuts.dart';
|
||||
|
||||
|
||||
class KeyboardShortcutsPage extends StatefulWidget {
|
||||
const KeyboardShortcutsPage({super.key, required this.shortcuts, required this.onSave});
|
||||
const KeyboardShortcutsPage(
|
||||
{super.key, required this.shortcuts, required this.onSave});
|
||||
|
||||
final KeyboardShortcuts shortcuts;
|
||||
final void Function(KeyboardShortcuts) onSave;
|
||||
@@ -17,23 +18,23 @@ class _KeyboardShortcutsPageState extends State<KeyboardShortcutsPage> {
|
||||
late KeyboardShortcuts shortucts;
|
||||
|
||||
final _controllers = {
|
||||
NumpadKey.numpad0: TextEditingController(),
|
||||
NumpadKey.numpad1: TextEditingController(),
|
||||
NumpadKey.numpad2: TextEditingController(),
|
||||
NumpadKey.numpad3: TextEditingController(),
|
||||
NumpadKey.numpad4: TextEditingController(),
|
||||
NumpadKey.numpad5: TextEditingController(),
|
||||
NumpadKey.numpad6: TextEditingController(),
|
||||
NumpadKey.numpad7: TextEditingController(),
|
||||
NumpadKey.numpad8: TextEditingController(),
|
||||
NumpadKey.numpad9: TextEditingController(),
|
||||
NumpadKey.numpadEnter: TextEditingController(),
|
||||
NumpadKey.numpadDecimal: TextEditingController(),
|
||||
NumpadKey.numpadAdd: TextEditingController(),
|
||||
NumpadKey.numpadSubtract: TextEditingController(),
|
||||
NumpadKey.numpadMultiply: TextEditingController(),
|
||||
NumpadKey.numpadDivide: TextEditingController(),
|
||||
NumpadKey.numpadEqual: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad0: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad1: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad2: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad3: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad4: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad5: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad6: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad7: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad8: TextEditingController(),
|
||||
LogicalKeyboardKey.numpad9: TextEditingController(),
|
||||
LogicalKeyboardKey.numpadEnter: TextEditingController(),
|
||||
LogicalKeyboardKey.numpadDecimal: TextEditingController(),
|
||||
LogicalKeyboardKey.numpadAdd: TextEditingController(),
|
||||
LogicalKeyboardKey.numpadSubtract: TextEditingController(),
|
||||
LogicalKeyboardKey.numpadMultiply: TextEditingController(),
|
||||
LogicalKeyboardKey.numpadDivide: TextEditingController(),
|
||||
LogicalKeyboardKey.numpadEqual: TextEditingController(),
|
||||
};
|
||||
|
||||
@override
|
||||
@@ -52,35 +53,35 @@ class _KeyboardShortcutsPageState extends State<KeyboardShortcutsPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ordered = <List<NumpadKey?>>[
|
||||
final ordered = <List<LogicalKeyboardKey?>>[
|
||||
[
|
||||
null,
|
||||
null,
|
||||
NumpadKey.numpadDivide,
|
||||
NumpadKey.numpadMultiply,
|
||||
LogicalKeyboardKey.numpadDivide,
|
||||
LogicalKeyboardKey.numpadMultiply,
|
||||
],
|
||||
[
|
||||
NumpadKey.numpad7,
|
||||
NumpadKey.numpad8,
|
||||
NumpadKey.numpad9,
|
||||
NumpadKey.numpadSubtract
|
||||
LogicalKeyboardKey.numpad7,
|
||||
LogicalKeyboardKey.numpad8,
|
||||
LogicalKeyboardKey.numpad9,
|
||||
LogicalKeyboardKey.numpadSubtract
|
||||
],
|
||||
[
|
||||
NumpadKey.numpad4,
|
||||
NumpadKey.numpad5,
|
||||
NumpadKey.numpad6,
|
||||
NumpadKey.numpadAdd
|
||||
LogicalKeyboardKey.numpad4,
|
||||
LogicalKeyboardKey.numpad5,
|
||||
LogicalKeyboardKey.numpad6,
|
||||
LogicalKeyboardKey.numpadAdd
|
||||
],
|
||||
[
|
||||
NumpadKey.numpad1,
|
||||
NumpadKey.numpad2,
|
||||
NumpadKey.numpad3,
|
||||
NumpadKey.numpadEqual
|
||||
LogicalKeyboardKey.numpad1,
|
||||
LogicalKeyboardKey.numpad2,
|
||||
LogicalKeyboardKey.numpad3,
|
||||
LogicalKeyboardKey.numpadEqual
|
||||
],
|
||||
[
|
||||
NumpadKey.numpad0,
|
||||
LogicalKeyboardKey.numpad0,
|
||||
null,
|
||||
NumpadKey.numpadDecimal,
|
||||
LogicalKeyboardKey.numpadDecimal,
|
||||
null,
|
||||
],
|
||||
];
|
||||
@@ -113,8 +114,8 @@ class _KeyboardShortcutsPageState extends State<KeyboardShortcutsPage> {
|
||||
controller: _controllers[key],
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText:
|
||||
numpadKeyLabels[key] ?? key.name,
|
||||
labelText: numpadKeyLabels[key] ??
|
||||
key.keyLabel,
|
||||
floatingLabelBehavior:
|
||||
FloatingLabelBehavior.always,
|
||||
),
|
||||
|
||||
12
pubspec.yaml
12
pubspec.yaml
@@ -111,6 +111,18 @@ flutter:
|
||||
- family: Menlo
|
||||
fonts:
|
||||
- asset: assets/fonts/Menlo.ttc
|
||||
- family: Fira Code
|
||||
fonts:
|
||||
- asset: assets/fonts/FiraCode-Regular.ttf
|
||||
- asset: assets/fonts/FiraCode-Bold.ttf
|
||||
weight: 700
|
||||
- asset: assets/fonts/FiraCode-SemiBold.ttf
|
||||
weight: 600
|
||||
- asset: assets/fonts/FiraCode-Medium.ttf
|
||||
weight: 500
|
||||
- asset: assets/fonts/FiraCode-Light.ttf
|
||||
weight: 300
|
||||
# - asset: assets/fonts/FiraCode-Retina.ttf
|
||||
|
||||
flutter_native_splash:
|
||||
color: '#fcf5e5'
|
||||
|
||||
Reference in New Issue
Block a user