Files
COPILOT/copilot_keyboard/lib/presentation/widgets/toolbar/bottom_toolbar.dart
klas 40143734fc Initial commit: COPILOT D6 Flutter keyboard controller
Flutter web app replacing legacy WPF CCTV surveillance keyboard controller.
Includes wall overview, section view with monitor grid, camera input,
PTZ control, alarm/lock/sequence BLoCs, and legacy-matching UI styling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:57:38 +01:00

304 lines
9.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../blocs/wall/wall_bloc.dart';
import '../../blocs/wall/wall_state.dart';
import 'camera_input_widget.dart';
import 'function_buttons_widget.dart';
/// Bottom toolbar with camera input and action buttons
class BottomToolbar extends StatelessWidget {
final VoidCallback? onSearchPressed;
final VoidCallback? onPrepositionPressed;
final VoidCallback? onPlaybackPressed;
final VoidCallback? onAlarmListPressed;
final VoidCallback? onLockPressed;
final VoidCallback? onSequencePressed;
final Function(String)? onFunctionButtonPressed;
const BottomToolbar({
super.key,
this.onSearchPressed,
this.onPrepositionPressed,
this.onPlaybackPressed,
this.onAlarmListPressed,
this.onLockPressed,
this.onSequencePressed,
this.onFunctionButtonPressed,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
decoration: const BoxDecoration(
color: Color(0xFF1A202C),
border: Border(
top: BorderSide(color: Color(0xFF4A5568), width: 1),
),
),
child: BlocBuilder<WallBloc, WallState>(
builder: (context, state) {
final hasSelection = state.selectedViewerId != null;
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// Top row - Camera input and function buttons
Row(
children: [
// Camera input
const Expanded(
child: CameraInputWidget(),
),
const SizedBox(width: 24),
// Function buttons
FunctionButtonsWidget(
onButtonPressed: onFunctionButtonPressed,
),
],
),
const SizedBox(height: 12),
// Bottom row - Context actions
Row(
children: [
if (hasSelection) ...[
// Search button
_ActionButton(
icon: Icons.search,
label: 'Hledat',
onPressed: onSearchPressed,
),
const SizedBox(width: 8),
// Preposition button
_ActionButton(
icon: Icons.place,
label: 'Prepozice',
onPressed: onPrepositionPressed,
),
const SizedBox(width: 8),
// Playback button
_ActionButton(
icon: Icons.history,
label: 'PvZ',
onPressed: onPlaybackPressed,
),
const SizedBox(width: 8),
// Alarm list button
_ActionButton(
icon: Icons.notifications,
label: 'Alarmy',
onPressed: onAlarmListPressed,
),
const SizedBox(width: 8),
// Sequence button
_ActionButton(
icon: Icons.loop,
label: 'Sekvence',
onPressed: onSequencePressed,
),
const SizedBox(width: 8),
// Lock button
_LockButton(
viewerState: state.getViewerState(state.selectedViewerId!),
onPressed: onLockPressed,
),
] else ...[
// Show selection hint when nothing selected
const Expanded(
child: Text(
'Vyberte monitor pro zobrazení akcí',
style: TextStyle(
color: Color(0xFF718096),
fontSize: 14,
),
),
),
],
const Spacer(),
// Selected monitor info
if (hasSelection)
_SelectionInfo(
viewerId: state.selectedViewerId!,
viewerState: state.getViewerState(state.selectedViewerId!),
),
],
),
],
);
},
),
);
}
}
class _ActionButton extends StatelessWidget {
final IconData icon;
final String label;
final VoidCallback? onPressed;
const _ActionButton({
required this.icon,
required this.label,
this.onPressed,
});
@override
Widget build(BuildContext context) {
return Material(
color: const Color(0xFF2D3748),
borderRadius: BorderRadius.circular(4),
child: InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(4),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
color: onPressed != null ? Colors.white : const Color(0xFF4A5568),
size: 18,
),
const SizedBox(width: 6),
Text(
label,
style: TextStyle(
color: onPressed != null ? Colors.white : const Color(0xFF4A5568),
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
);
}
}
class _LockButton extends StatelessWidget {
final ViewerState viewerState;
final VoidCallback? onPressed;
const _LockButton({
required this.viewerState,
this.onPressed,
});
@override
Widget build(BuildContext context) {
final isLocked = viewerState.isLocked;
final isLockedByOther = viewerState.isLockedByOther;
return Material(
color: isLocked
? (isLockedByOther ? const Color(0xFFDC2626) : const Color(0xFF38A169))
: const Color(0xFF2D3748),
borderRadius: BorderRadius.circular(4),
child: InkWell(
onTap: isLockedByOther ? null : onPressed,
borderRadius: BorderRadius.circular(4),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isLocked ? Icons.lock : Icons.lock_open,
color: Colors.white,
size: 18,
),
const SizedBox(width: 6),
Text(
isLocked ? 'Zamčeno' : 'Zamknout',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
);
}
}
class _SelectionInfo extends StatelessWidget {
final int viewerId;
final ViewerState viewerState;
const _SelectionInfo({
required this.viewerId,
required this.viewerState,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: const Color(0xFF2D3748),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: const Color(0xFF00D4FF), width: 1),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.tv,
color: Color(0xFF00D4FF),
size: 16,
),
const SizedBox(width: 8),
Text(
'Monitor $viewerId',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
if (viewerState.hasCamera) ...[
const SizedBox(width: 12),
const Icon(
Icons.videocam,
color: Colors.white70,
size: 16,
),
const SizedBox(width: 4),
Text(
'${viewerState.currentCameraId}',
style: const TextStyle(
color: Colors.white70,
fontSize: 14,
),
),
],
if (viewerState.hasAlarm) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: const Color(0xFFDC2626),
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'ALARM',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
),
],
],
),
);
}
}