Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/models/section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ class Section {
bool getIsSelected() => isSelected;
void setIsSelected(bool selected) => isSelected = selected;

/// Toggle selection state
void toggleSelection() => isSelected = !isSelected;

double getLength() => length;
double getDepthStart() => depthStart;
double getDepthEnd() => depthEnd;
Expand Down
68 changes: 60 additions & 8 deletions lib/models/section_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,35 @@ class SectionList {
/// Get only selected sections
List<Section> get selectedSections => sections.where((section) => section.isSelected).toList();

/// Get selection statistics in a single pass for better performance
SelectionStats get selectionStats {
if (sections.isEmpty) return SelectionStats.empty();

int selectedCount = 0;
for (final section in sections) {
if (section.isSelected) selectedCount++;
}

return SelectionStats(
total: sections.length,
selected: selectedCount,
);
}

/// Check if all sections are selected
bool get allSelected => sections.isNotEmpty && sections.every((section) => section.isSelected);
bool get allSelected {
final stats = selectionStats;
return stats.total > 0 && stats.selected == stats.total;
}

/// Check if no sections are selected
bool get noneSelected => sections.every((section) => !section.isSelected);
bool get noneSelected => selectionStats.selected == 0;

/// Check if some sections are selected (for partial selection state)
bool get someSelected => sections.any((section) => section.isSelected) && !allSelected;
bool get someSelected {
final stats = selectionStats;
return stats.selected > 0 && stats.selected < stats.total;
}

/// Select all sections
void selectAll() {
Expand All @@ -66,16 +87,47 @@ class SectionList {
}
}

/// Toggle selection for all sections
/// Toggle selection for all sections (optimized)
void toggleSelectAll() {
if (allSelected) {
deselectAll();
} else {
selectAll();
final shouldSelectAll = selectionStats.selected < sections.length;
for (final section in sections) {
section.isSelected = shouldSelectAll;
}
}

/// Set selection state for multiple sections efficiently
void setSelectionForSections(List<Section> sectionsToUpdate, bool selected) {
for (final section in sectionsToUpdate) {
section.isSelected = selected;
}
}

/// Legacy getters/setters for compatibility with existing code
List<Section> getSections() => sections;
void setSections(List<Section> newSections) => sections = newSections;
}

/// Efficient statistics for section selection state
class SelectionStats {
final int total;
final int selected;

const SelectionStats({required this.total, required this.selected});

const SelectionStats.empty() : total = 0, selected = 0;

/// Get number of unselected sections
int get unselected => total - selected;

/// Check if all are selected
bool get allSelected => total > 0 && selected == total;

/// Check if none are selected
bool get noneSelected => selected == 0;

/// Check if some are selected
bool get someSelected => selected > 0 && selected < total;

@override
String toString() => 'SelectionStats(selected: $selected, total: $total)';
}
190 changes: 108 additions & 82 deletions lib/widgets/data_toolbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,89 +39,115 @@ class DataToolbar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AppBar(
// Left side - Input functions
leading: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Select all/deselect all button
if (hasSections)
IconButton(
onPressed: onToggleSelectAll,
icon: Icon(allSectionsSelected ? Icons.deselect : Icons.select_all),
tooltip: allSectionsSelected ? "Deselect All" : "Select All",
),

// Clear data button
IconButton(
onPressed: !hasSections ? null : onReset,
icon: const Icon(Icons.backspace_rounded),
tooltip: "Clear local Data",
),

// Read data from device button
IconButton(
onPressed: (serialBusy || !connected) ? null : onReadData,
icon: const Icon(Icons.download_rounded),
tooltip: "Read Data from Device",
),

// Open DMP file button
FileIcon(
onPressed: serialBusy ? null : onOpenDMP,
icon: Icons.file_open,
extension: 'DMP',
tooltip: "Open DMP file",
size: 24,
color: serialBusy ? Colors.black26 : Colors.black54,
extensionColor: serialBusy ? Colors.black26 : Colors.black87,
),
],
),
leadingWidth: hasSections ? 240 : 192, // Adjust width based on whether select all button is shown

// Right side - Export functions
actions: [
// Save DMP file button
FileIcon(
onPressed: (serialBusy || !hasSelectedSections) ? null : onSaveDMP,
extension: 'DMP',
tooltip: "Save selected surveys as DMP",
size: 24,
color: (serialBusy || !hasSelectedSections) ? Colors.black26 : Colors.black54,
extensionColor: (serialBusy || !hasSelectedSections) ? Colors.black26 : Colors.black87,
),

// Export to Excel button
FileIcon(
onPressed: (serialBusy || !hasSelectedSections) ? null : onExportXLS,
extension: 'XLS',
tooltip: "Export selected surveys as Excel",
size: 24,
color: (serialBusy || !hasSelectedSections) ? Colors.black26 : Colors.black54,
extensionColor: (serialBusy || !hasSelectedSections) ? Colors.black26 : Colors.black87,
),

// Export to Survex button
FileIcon(
onPressed: (serialBusy || !hasSelectedSections) ? null : onExportSVX,
extension: 'SVX',
tooltip: "Export selected surveys as Survex",
size: 24,
color: (serialBusy || !hasSelectedSections) ? Colors.black26 : Colors.black54,
extensionColor: (serialBusy || !hasSelectedSections) ? Colors.black26 : Colors.black87,
),

// Export to Therion button
FileIcon(
onPressed: (serialBusy || !hasSelectedSections) ? null : onExportTH,
extension: 'TH',
tooltip: "Export selected surveys as Therion",
size: 24,
color: (serialBusy || !hasSelectedSections) ? Colors.black26 : Colors.black54,
extensionColor: (serialBusy || !hasSelectedSections) ? Colors.black26 : Colors.black87,
),
],
leading: _buildInputActions(),
leadingWidth: hasSections ? 240 : 192,
actions: _buildExportActions(),
backgroundColor: Colors.white30,
);
}

Widget _buildInputActions() {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if (hasSections) _buildSelectAllButton(),
_buildClearDataButton(),
_buildReadDataButton(),
_buildOpenDMPButton(),
],
);
}

Widget _buildSelectAllButton() {
return IconButton(
onPressed: onToggleSelectAll,
icon: Icon(allSectionsSelected ? Icons.deselect : Icons.select_all),
tooltip: allSectionsSelected ? "Deselect All" : "Select All",
);
}

Widget _buildClearDataButton() {
return IconButton(
onPressed: !hasSections ? null : onReset,
icon: const Icon(Icons.backspace_rounded),
tooltip: "Clear local Data",
);
}

Widget _buildReadDataButton() {
return IconButton(
onPressed: (serialBusy || !connected) ? null : onReadData,
icon: const Icon(Icons.download_rounded),
tooltip: "Read Data from Device",
);
}

Widget _buildOpenDMPButton() {
return FileIcon(
onPressed: serialBusy ? null : onOpenDMP,
icon: Icons.file_open,
extension: 'DMP',
tooltip: "Open DMP file",
size: 24,
color: serialBusy ? Colors.black26 : Colors.black54,
extensionColor: serialBusy ? Colors.black26 : Colors.black87,
);
}

List<Widget> _buildExportActions() {
return [
_buildSaveDMPButton(),
_buildExportExcelButton(),
_buildExportSurvexButton(),
_buildExportTherionButton(),
];
}

Widget _buildSaveDMPButton() {
return _buildExportFileIcon(
onPressed: onSaveDMP,
extension: 'DMP',
tooltip: "Save selected surveys as DMP",
);
}

Widget _buildExportExcelButton() {
return _buildExportFileIcon(
onPressed: onExportXLS,
extension: 'XLS',
tooltip: "Export selected surveys as Excel",
);
}

Widget _buildExportSurvexButton() {
return _buildExportFileIcon(
onPressed: onExportSVX,
extension: 'SVX',
tooltip: "Export selected surveys as Survex",
);
}

Widget _buildExportTherionButton() {
return _buildExportFileIcon(
onPressed: onExportTH,
extension: 'TH',
tooltip: "Export selected surveys as Therion",
);
}

Widget _buildExportFileIcon({
required VoidCallback? onPressed,
required String extension,
required String tooltip,
}) {
final isEnabled = !serialBusy && hasSelectedSections;
return FileIcon(
onPressed: isEnabled ? onPressed : null,
extension: extension,
tooltip: tooltip,
size: 24,
color: isEnabled ? Colors.black54 : Colors.black26,
extensionColor: isEnabled ? Colors.black87 : Colors.black26,
);
}
}
Loading
Loading