feat: terminal type, alias/trigger updates

fix: fonts/colors
This commit is contained in:
2023-10-17 03:12:57 +03:00
parent 97147696ed
commit 53f59174c4
16 changed files with 492 additions and 361 deletions

BIN
assets/fonts/FiraCode-Bold.ttf Executable file

Binary file not shown.

BIN
assets/fonts/FiraCode-Light.ttf Executable file

Binary file not shown.

BIN
assets/fonts/FiraCode-Medium.ttf Executable file

Binary file not shown.

BIN
assets/fonts/FiraCode-Regular.ttf Executable file

Binary file not shown.

BIN
assets/fonts/FiraCode-Retina.ttf Executable file

Binary file not shown.

Binary file not shown.

View File

@@ -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,

View File

@@ -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>[

View File

@@ -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: '=',
};

View File

@@ -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});
}

View File

@@ -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;
}

View File

@@ -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> {

View File

@@ -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(

View File

@@ -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);
}
},
),
],
],
),
),
),
);

View File

@@ -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,
),

View File

@@ -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'