Skip to content

Commit

Permalink
made table columns sortable & searchable; + misc changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ArthurHeitmann committed Oct 23, 2022
1 parent 96e74bb commit 8e4b8ac
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 71 deletions.
5 changes: 4 additions & 1 deletion lib/stateManagement/charNamesXmlWrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ class CharNamesXmlProp extends XmlProp with CustomTableConfig {
var spaceIndex = line.indexOf(" ");
var key = line.substring(0, spaceIndex);
var val = line.substring(spaceIndex + 1);
currentTranslations.add(KeyValProp(StringProp(key), StringProp(val)));
var keyProp = StringProp(key);
var valProp = StringProp(val);
valProp.transform = (str) => str;
currentTranslations.add(KeyValProp(keyProp, valProp));
}
else {
if (currentKey != "") {
Expand Down
22 changes: 15 additions & 7 deletions lib/stateManagement/otherFileTypes/TmdFileData.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ class TmdData extends NestedNotifier<TmdEntryData> with CustomTableConfig, Undoa
TmdData.from(List<TmdEntry> rawEntries, String fileName)
: fileChangeNotifier = ChangeNotifier(),
super([]) {
addAll(rawEntries.map((e) => TmdEntryData(
id: StringProp(e.id),
text: StringProp(e.text),
anyChangeNotifier: fileChangeNotifier,
)));
addAll(rawEntries.map((e) {
var idProp = StringProp(e.id);
var textProp = StringProp(e.text);
textProp.transform = (str) => str;
return TmdEntryData(
id: idProp,
text: textProp,
anyChangeNotifier: fileChangeNotifier,
);
}));
name = fileName;
columnNames = ["ID", "Text"];
rowCount = NumberProp(rawEntries.length, true);
Expand All @@ -50,9 +55,12 @@ class TmdData extends NestedNotifier<TmdEntryData> with CustomTableConfig, Undoa

@override
void onRowAdd() {
var idProp = StringProp("ID");
var textProp = StringProp("Text");
textProp.transform = (str) => str;
add(TmdEntryData(
id: StringProp("TMD ID"),
text: StringProp("Text"),
id: idProp,
text: textProp,
anyChangeNotifier: fileChangeNotifier,
));
rowCount.value++;
Expand Down
2 changes: 1 addition & 1 deletion lib/stateManagement/sync/syncObjects.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void startSyncingObject(SyncedObject obj) {
obj.startSync();
}
else {
showToast("Can't sync! Probably no connection to Blender");
showToast("Can't sync! Connect from Blender first");
}

}
Expand Down
2 changes: 2 additions & 0 deletions lib/widgets/FileHierarchyExplorer/HierarchyEntryWidget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class _HierarchyEntryState extends ChangeNotifierState<HierarchyEntryWidget> {
return Icon(Icons.workspaces, color: iconColor, size: 15);
else if (widget.entry is XmlScriptHierarchyEntry || widget.entry is RubyScriptHierarchyEntry)
return Icon(Icons.description, color: iconColor, size: 15);
else if (widget.entry is TmdHierarchyEntry)
return Icon(Icons.subtitles, color: iconColor, size: 15);

return null;
}
Expand Down
1 change: 1 addition & 0 deletions lib/widgets/filesView/TextFileEditor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class _TextFileEditorState extends ChangeNotifierState<TextFileEditor> {
// theme: _customTheme,
theme: atomOneDarkTheme,
language: _highlightLanguages[extension(widget.fileContent.path)],
params: EditorParams(tabSpaces: 4),
text: widget.fileContent.text,
onChange: (text) {
if (text == widget.fileContent.text)
Expand Down
159 changes: 141 additions & 18 deletions lib/widgets/propEditors/customXmlProps/tableEditor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import '../../../widgets/theme/customTheme.dart';
import '../../../stateManagement/ChangeNotifierWidget.dart';
import '../../../stateManagement/Property.dart';
import '../../misc/nestedContextMenu.dart';
import '../simpleProps/UnderlinePropTextField.dart';
import '../simpleProps/propEditorFactory.dart';
import '../simpleProps/propTextField.dart';
import '../simpleProps/transparentPropTextField.dart';
Expand All @@ -23,6 +24,12 @@ class RowConfig {

RowConfig({ required this.key, required this.cells });
}
class _RowConfigIndexed {
final int index;
final RowConfig rowConfig;

const _RowConfigIndexed(this.index, this.rowConfig);
}

mixin CustomTableConfig {
late final String name;
Expand All @@ -34,6 +41,13 @@ mixin CustomTableConfig {
void onRowRemove(int index);
}

class _ColumnSort {
int index;
bool ascending;

_ColumnSort(this.index, this.ascending);
}

class TableEditor extends ChangeNotifierWidget {
final CustomTableConfig config;

Expand All @@ -45,9 +59,78 @@ class TableEditor extends ChangeNotifierWidget {

class _TableEditorState extends ChangeNotifierState<TableEditor> {
final scrollController = ScrollController();
List<_RowConfigIndexed> rows = [];
_ColumnSort? columnSort;
late final List<StringProp> columnSearch;

@override
void initState() {
columnSearch = List.generate(widget.config.columnNames.length, (index) {
var prop = StringProp("");
prop.addListener(() => setState(() {}));
return prop;
});
super.initState();
}

@override
void dispose() {
for (var prop in columnSearch)
prop.dispose();
super.dispose();
}

void updateRows() {
// generate all rows
rows = List.generate(
widget.config.rowCount.value as int,
(index) => _RowConfigIndexed(index, widget.config.rowPropsGenerator(index))
);
// filter rows
if (columnSearch.any((prop) => prop.value.isNotEmpty)) {
rows = rows.where((row) {
for (var i = 0; i < columnSearch.length; i++) {
if (columnSearch[i].value.isEmpty)
continue;
var cell = row.rowConfig.cells[i];
if (cell == null) {
if (columnSearch[i].value.isNotEmpty)
return false;
continue;
}
var searchStr = columnSearch[i].value.toLowerCase();
var cellValue = cell.prop.toString().toLowerCase();
if (!cellValue.contains(searchStr))
return false;
}
return true;
}).toList();
}
// sort rows
if (columnSort != null) {
rows.sort((a, b) {
final aCell = a.rowConfig.cells[columnSort!.index];
final bCell = b.rowConfig.cells[columnSort!.index];
if (aCell == null && bCell == null)
return 0;
if (aCell == null)
return columnSort!.ascending ? -1 : 1;
if (bCell == null)
return columnSort!.ascending ? 1 : -1;
final aProp = aCell.prop;
final bProp = bCell.prop;
if (aProp is NumberProp && bProp is NumberProp) {
return aProp.value.compareTo(bProp.value) * (columnSort!.ascending ? 1 : -1);
} else {
return aProp.toString().compareTo(bProp.toString()) * (columnSort!.ascending ? 1 : -1);
}
});
}
}

@override
Widget build(BuildContext context) {
updateRows();
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
Expand Down Expand Up @@ -86,7 +169,6 @@ class _TableEditorState extends ChangeNotifierState<TableEditor> {
for (int i = 0; i < widget.config.columnNames.length; i++)
Expanded(
child: Container(
height: 60,
decoration: BoxDecoration(
color: getTheme(context).tableBgColor,
border: Border(
Expand All @@ -96,19 +178,56 @@ class _TableEditorState extends ChangeNotifierState<TableEditor> {
),
),
),
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text(
widget.config.columnNames[i],
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
padding: const EdgeInsets.only(left: 8, right: 2, top: 4, bottom: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextButton(
onPressed: () {
setState(() {
if (columnSort != null && columnSort!.index == i) {
if (columnSort!.ascending)
columnSort!.ascending = false;
else
columnSort = null;
} else {
columnSort = _ColumnSort(i, true);
}
});
},
child: Row(
children: [
Flexible(
child: Text(
widget.config.columnNames[i],
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
overflow: TextOverflow.ellipsis,
),
),
SizedBox(width: 6),
columnSort == null || columnSort!.index != i
? Icon(Icons.swap_vert, size: 17, color: Theme.of(context).colorScheme.primary.withOpacity(0.5))
: columnSort!.ascending
? Icon(Icons.arrow_drop_up, size: 20)
: Icon(Icons.arrow_drop_down, size: 20),
],
),
overflow: TextOverflow.ellipsis,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 4, right: 6),
child: makePropEditor<UnderlinePropTextField>(
columnSearch[i],
PropTFOptions(
constraints: BoxConstraints(maxWidth: 200),
useIntrinsicWidth: false,
hintText: "Search...",
)
),
),
],
),
)
),
Expand All @@ -132,8 +251,8 @@ class _TableEditorState extends ChangeNotifierState<TableEditor> {
children: [
ListView.builder(
controller: scrollController,
itemCount: widget.config.rowCount.value as int,
itemBuilder: (context, i) => _TableRow(index: i, config: widget.config),
itemCount: rows.length,
itemBuilder: (context, i) => _TableRow(i % 2 == 1, rows[i].index, rows[i].rowConfig, widget.config),
),
_makeAddRowButton()
],
Expand All @@ -157,6 +276,8 @@ class _TableEditorState extends ChangeNotifierState<TableEditor> {
onPressed: () {
widget.config.onRowAdd();
setState(() {});
if (columnSort != null)
return;
WidgetsBinding.instance.addPostFrameCallback((_) {
scrollController.animateTo(
scrollController.position.maxScrollExtent,
Expand All @@ -174,10 +295,13 @@ class _TableEditorState extends ChangeNotifierState<TableEditor> {
}

class _TableRow extends StatefulWidget {
final bool alt;
final int index;
final RowConfig row;
final CustomTableConfig config;

const _TableRow({ required this.index, required this.config });
_TableRow(this.alt, this.index, this.row, this.config)
: super(key: row.key);

@override
State<_TableRow> createState() => _TableRowState();
Expand All @@ -188,12 +312,11 @@ class _TableRowState extends State<_TableRow> {

@override
Widget build(BuildContext context) {
var rowColor = widget.index % 2 == 1 ? Colors.transparent : getTheme(context).tableBgAltColor;
var rowColor = widget.alt ? Colors.transparent : getTheme(context).tableBgAltColor;
if (isHovered)
rowColor = getTheme(context).textColor!.withOpacity(0.2);

return NestedContextMenu(
key: widget.config.rowPropsGenerator(widget.index).key,
clearParent: true,
buttons: [
ContextMenuButtonConfig(
Expand All @@ -210,7 +333,7 @@ class _TableRowState extends State<_TableRow> {
child: Row(
children: [
for (int j = 0; j < widget.config.columnNames.length; j++)
makeCell(cell: widget.config.rowPropsGenerator(widget.index).cells[j], drawBorder: j != widget.config.columnNames.length - 1),
makeCell(cell: widget.row.cells[j], drawBorder: j != widget.config.columnNames.length - 1),
],
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class _DoubleClickablePropTextFieldState extends PropTextFieldState {
scrollController: ScrollController(keepScrollOffset: false),
maxLines: widget.options.isMultiline ? null : 1,
keyboardType: widget.options.isMultiline ? TextInputType.multiline : null,
decoration: InputDecoration(
hintText: widget.options.hintText,
),
),
)
: Padding(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class _UnderlinePropTextFieldState extends PropTextFieldState {
scrollController: ScrollController(keepScrollOffset: false),
maxLines: widget.options.isMultiline ? null : 1,
keyboardType: widget.options.isMultiline ? TextInputType.multiline : null,
decoration: InputDecoration(
hintText: widget.options.hintText,
),
),
),
if (errorMsg != null)
Expand Down
3 changes: 3 additions & 0 deletions lib/widgets/propEditors/simpleProps/primaryPropTextField.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class _PrimaryPropTextFieldState extends PropTextFieldState {
scrollController: ScrollController(keepScrollOffset: false),
maxLines: widget.options.isMultiline ? null : 1,
keyboardType: widget.options.isMultiline ? TextInputType.multiline : null,
decoration: InputDecoration(
hintText: widget.options.hintText,
),
),
),
if (errorMsg != null)
Expand Down
4 changes: 3 additions & 1 deletion lib/widgets/propEditors/simpleProps/propTextField.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ class PropTFOptions {
final BoxConstraints constraints;
final bool isMultiline;
final bool useIntrinsicWidth;
final String? hintText;

const PropTFOptions({
this.constraints = const BoxConstraints(minWidth: 50),
this.isMultiline = false,
this.useIntrinsicWidth = true
this.useIntrinsicWidth = true,
this.hintText,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ class _TransparentPropTextFieldState extends PropTextFieldState {
onChanged: onTextChange,
style: getTheme(context).propInputTextStyle,
scrollController: ScrollController(keepScrollOffset: false),
maxLines: widget.options.isMultiline ? null : 1,
keyboardType: widget.options.isMultiline ? TextInputType.multiline : null,
maxLines: widget.options.isMultiline ? null : 1,
keyboardType: widget.options.isMultiline ? TextInputType.multiline : null,
decoration: InputDecoration(
hintText: widget.options.hintText,
),
),
),
if (errorMsg != null)
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/theme/customTheme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class NierThemeExtension extends ThemeExtension<NierThemeExtension> {
}

Color colorOfFiletype(HierarchyEntry entry) {
if (entry is XmlScriptHierarchyEntry || entry is RubyScriptHierarchyEntry)
if (entry is XmlScriptHierarchyEntry || entry is RubyScriptHierarchyEntry || entry is TmdHierarchyEntry)
return filetypeXmlColor!;
if (entry is PakHierarchyEntry)
return filetypePakColor!;
Expand Down
Loading

0 comments on commit 8e4b8ac

Please sign in to comment.