Clean up shit
This commit is contained in:
219
lib/main.dart
219
lib/main.dart
@@ -1,34 +1,27 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io'; // Required for Platform check
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart'; // Needed for LogicalKeyboardKey
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:system_tray/system_tray.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
// For path joining
|
||||
|
||||
// --- Configuration ---
|
||||
const Duration popupInterval = Duration(hours: 1); // How often to pop up
|
||||
const Duration popupInterval = Duration(hours: 1);
|
||||
const String notificationSound = 'MeetTheSniper.mp3';
|
||||
// --------------------
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
// Must add this line.
|
||||
await windowManager.ensureInitialized();
|
||||
|
||||
// Use it only after calling `hiddenWindowAtLaunch`
|
||||
WindowOptions windowOptions = const WindowOptions(
|
||||
size: Size(1600, 900), // Increased size to 1600x900
|
||||
size: Size(1600, 900),
|
||||
center: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
skipTaskbar: false,
|
||||
titleBarStyle: TitleBarStyle.normal,
|
||||
);
|
||||
|
||||
// Hide window at launch
|
||||
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
await windowManager.hide(); // Start hidden
|
||||
await windowManager.hide();
|
||||
});
|
||||
|
||||
runApp(const JournalerApp());
|
||||
@@ -37,10 +30,8 @@ void main() async {
|
||||
class JournalerApp extends StatelessWidget {
|
||||
const JournalerApp({super.key});
|
||||
|
||||
// --- Base Theme Configuration ---
|
||||
static final TextTheme _baseTextTheme = const TextTheme(
|
||||
bodyMedium: TextStyle(fontSize: 24),
|
||||
// Define other common styles if needed
|
||||
);
|
||||
|
||||
static final TextStyle _baseTitleTextStyle = const TextStyle(
|
||||
@@ -48,15 +39,12 @@ class JournalerApp extends StatelessWidget {
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
|
||||
static final InputDecorationTheme
|
||||
_baseInputDecorationTheme = const InputDecorationTheme(
|
||||
border: OutlineInputBorder(),
|
||||
labelStyle: TextStyle(fontSize: 20), // Base label size
|
||||
// contentPadding: EdgeInsets.all(12.0), // Example: Add padding globally
|
||||
);
|
||||
// ------------------------------
|
||||
static final InputDecorationTheme _baseInputDecorationTheme =
|
||||
const InputDecorationTheme(
|
||||
border: OutlineInputBorder(),
|
||||
labelStyle: TextStyle(fontSize: 20),
|
||||
);
|
||||
|
||||
// Define the light theme
|
||||
static final ThemeData lightTheme = ThemeData.light().copyWith(
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
@@ -77,12 +65,9 @@ class JournalerApp extends StatelessWidget {
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
textTheme: _baseTextTheme.copyWith(
|
||||
// Optionally override specific light theme text styles here
|
||||
),
|
||||
textTheme: _baseTextTheme,
|
||||
);
|
||||
|
||||
// Define the dark theme
|
||||
static final ThemeData darkTheme = ThemeData.dark().copyWith(
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
@@ -97,7 +82,7 @@ class JournalerApp extends StatelessWidget {
|
||||
),
|
||||
inputDecorationTheme: _baseInputDecorationTheme.copyWith(
|
||||
border: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey), // Dark theme default border
|
||||
borderSide: BorderSide(color: Colors.grey),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blue.shade300, width: 2.0),
|
||||
@@ -107,9 +92,7 @@ class JournalerApp extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
textTheme: _baseTextTheme.copyWith(
|
||||
// Ensure body text is readable on dark background
|
||||
bodyMedium: _baseTextTheme.bodyMedium?.copyWith(color: Colors.white),
|
||||
// Optionally override other dark theme text styles here
|
||||
),
|
||||
);
|
||||
|
||||
@@ -143,20 +126,16 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
final TextEditingController _todoController = TextEditingController();
|
||||
|
||||
Timer? _popupTimer;
|
||||
Timer? _debounceTimer; // Timer for debouncing Todo saves
|
||||
Timer? _debounceTimer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
windowManager.addListener(this);
|
||||
_initSystemTray();
|
||||
_loadData(); // Placeholder for loading initial data
|
||||
_loadData();
|
||||
_startPopupTimer();
|
||||
// Prevent window from closing, instead hide it
|
||||
// Needs `implements WindowListener` and `windowManager.addListener(this);`
|
||||
// and `windowManager.removeListener(this);` in dispose
|
||||
windowManager.setPreventClose(true);
|
||||
// Set aspect ratio after ensuring initialized
|
||||
_setWindowConfig();
|
||||
}
|
||||
|
||||
@@ -164,7 +143,7 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
void dispose() {
|
||||
windowManager.removeListener(this);
|
||||
_popupTimer?.cancel();
|
||||
_debounceTimer?.cancel(); // Cancel debounce timer on dispose
|
||||
_debounceTimer?.cancel();
|
||||
_previousEntryController.dispose();
|
||||
_currentEntryController.dispose();
|
||||
_todoController.dispose();
|
||||
@@ -172,67 +151,30 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// --- Window Listener --- //
|
||||
@override
|
||||
void onWindowClose() {
|
||||
_saveData(); // Placeholder for saving data
|
||||
windowManager.hide(); // Hide instead of closing
|
||||
// Alternatively, to fully close:
|
||||
// windowManager.destroy();
|
||||
_saveData();
|
||||
windowManager.hide();
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowFocus() {
|
||||
// Maybe reload data if needed when window becomes active
|
||||
setState(() {
|
||||
// If you need to refresh state
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
// --- System Tray --- //
|
||||
Future<void> _initSystemTray() async {
|
||||
// Use simple relative paths for assets declared in pubspec.yaml
|
||||
// The build process handles packaging these assets correctly.
|
||||
String iconPath = 'assets/app_icon.ico';
|
||||
|
||||
// IMPORTANT: You need to create an icon file named app_icon.ico (for Windows)
|
||||
// and/or app_icon.png (for Mac/Linux) and place it in the 'assets/' directory.
|
||||
// Create the 'assets' directory if it doesn't exist at the root of your project.
|
||||
// Then, ensure your pubspec.yaml lists the 'assets/' directory:
|
||||
// flutter:
|
||||
// uses-material-design: true
|
||||
// assets:
|
||||
// - assets/
|
||||
// - assets/sounds/ # Keep this if you have it
|
||||
await _systemTray.initSystemTray(iconPath: iconPath, toolTip: "Journaler");
|
||||
|
||||
// Check if the asset file exists before trying to use it (optional but recommended)
|
||||
// Note: This basic check works during development. Accessing assets might
|
||||
// require different handling in production builds depending on the platform.
|
||||
// try {
|
||||
// await rootBundle.load(iconPath); // Check if asset is loadable
|
||||
// } catch (_) {
|
||||
// debugPrint("Error: System tray icon '$iconPath' not found in assets.");
|
||||
// // Handle the error, maybe use a default icon or skip tray init
|
||||
// return;
|
||||
// }
|
||||
|
||||
await _systemTray.initSystemTray(
|
||||
// title: "Journaler", // Optional: Title shown on hover (might not work on all OS)
|
||||
iconPath: iconPath, // Use the corrected path
|
||||
toolTip: "Journaler",
|
||||
);
|
||||
|
||||
// Handle system tray menu item clicks
|
||||
_systemTray.registerSystemTrayEventHandler((eventName) {
|
||||
debugPrint("System Tray Event: $eventName");
|
||||
_showWindow();
|
||||
});
|
||||
}
|
||||
|
||||
// --- Periodic Popup & Sound --- //
|
||||
void _startPopupTimer() {
|
||||
_popupTimer = Timer.periodic(popupInterval, (timer) {
|
||||
// Timer now only requests the window to show
|
||||
_showWindow();
|
||||
});
|
||||
}
|
||||
@@ -240,28 +182,22 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
Future<void> _showWindow() async {
|
||||
bool wasVisible = await windowManager.isVisible();
|
||||
if (!wasVisible) {
|
||||
// Ensure size and position before showing
|
||||
await windowManager.setSize(const Size(1600, 900)); // Set desired size
|
||||
await windowManager.center(); // Center the window
|
||||
await windowManager.setSize(const Size(1600, 900));
|
||||
await windowManager.center();
|
||||
await windowManager.show();
|
||||
await windowManager.focus();
|
||||
// Play sound ONLY when window becomes visible
|
||||
await _playSound();
|
||||
} else {
|
||||
// If already visible, just bring to front and focus
|
||||
await windowManager.focus();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _playSound() async {
|
||||
try {
|
||||
// Stop any previous playback before starting anew
|
||||
await _audioPlayer.stop();
|
||||
// Assumes the sound file is in assets/sounds/
|
||||
await _audioPlayer.play(AssetSource('sounds/$notificationSound'));
|
||||
debugPrint("Played sound: $notificationSound");
|
||||
} catch (e, stackTrace) {
|
||||
// Catch stack trace for more details
|
||||
debugPrint("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
debugPrint("Error playing sound '$notificationSound': $e");
|
||||
debugPrint("Stack trace: $stackTrace");
|
||||
@@ -269,157 +205,94 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
"Ensure file exists, is valid audio, and assets/sounds/ is in pubspec.yaml",
|
||||
);
|
||||
debugPrint("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
// Handle error, e.g., show a notification or log
|
||||
}
|
||||
}
|
||||
|
||||
// --- Data Handling (Placeholders) --- //
|
||||
void _loadData() {
|
||||
// TODO: Implement logic to load previous entry and todo list
|
||||
// Example:
|
||||
// _previousEntryController.text = await loadPreviousEntryFromDatabase();
|
||||
// _todoController.text = await loadTodoListFromFile();
|
||||
_previousEntryController.text =
|
||||
"This is a placeholder for the previous entry.";
|
||||
_todoController.text = "- Placeholder Todo 1\n- Placeholder Todo 2";
|
||||
_currentEntryController.text = ""; // Current entry always starts empty
|
||||
_currentEntryController.text = "";
|
||||
debugPrint("Data loaded (placeholder).");
|
||||
}
|
||||
|
||||
void _saveData() {
|
||||
// TODO: Implement logic to save the current entry and todo list
|
||||
// This is called when the window is closed (hidden) or the save button is pressed.
|
||||
String currentEntry = _currentEntryController.text;
|
||||
String todoList = _todoController.text;
|
||||
|
||||
print(
|
||||
"Saving data (placeholder)... Current Entry: [${currentEntry.length} chars], Todo: [${todoList.length} chars]",
|
||||
);
|
||||
|
||||
// --- Your persistence logic goes here ---
|
||||
// Example:
|
||||
// await saveEntryToDatabase(currentEntry);
|
||||
// await saveTodoListToFile(todoList);
|
||||
// ---------------------------------------
|
||||
|
||||
// You might want to update the previous entry for the *next* session here,
|
||||
// or handle this logic when loading data next time.
|
||||
// Example (simplistic, assumes immediate update for next view):
|
||||
// setState(() {
|
||||
// _previousEntryController.text = currentEntry;
|
||||
// _currentEntryController.clear(); // Clear current entry after saving
|
||||
// });
|
||||
|
||||
// Potentially clear current entry after saving, or handle it on next load
|
||||
// _currentEntryController.clear(); // Decide if you want to clear it immediately
|
||||
}
|
||||
|
||||
// --- Add a specific save function for Todo List --- //
|
||||
void _saveTodoList() {
|
||||
// Cancel any existing timer
|
||||
if (_debounceTimer?.isActive ?? false) _debounceTimer!.cancel();
|
||||
|
||||
// Start a new timer
|
||||
_debounceTimer = Timer(const Duration(milliseconds: 500), () {
|
||||
// This code runs after 500ms of inactivity
|
||||
String todoList = _todoController.text;
|
||||
print("Debounced Save: Saving Todo list... [${todoList.length} chars]");
|
||||
// --- Your actual todo list persistence logic goes here ---
|
||||
// Example:
|
||||
// await saveTodoListToFile(todoList);
|
||||
// --------------------------------------------------------
|
||||
});
|
||||
}
|
||||
|
||||
// Helper to set initial window config like aspect ratio
|
||||
Future<void> _setWindowConfig() async {
|
||||
// Wait a moment to ensure window manager is fully ready after init
|
||||
// Might not be strictly necessary but can prevent race conditions
|
||||
// await Future.delayed(const Duration(milliseconds: 100));
|
||||
await windowManager.setAspectRatio(16 / 9);
|
||||
// Optionally set min/max size if desired
|
||||
// await windowManager.setMinimumSize(const Size(800, 450));
|
||||
}
|
||||
|
||||
// --- UI Build --- //
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Listen for keyboard events to close on Escape
|
||||
return RawKeyboardListener(
|
||||
focusNode: FocusNode(), // Need a focus node to receive keys
|
||||
autofocus: true, // Ensure it can receive focus
|
||||
onKey: (event) {
|
||||
if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||
return KeyboardListener(
|
||||
focusNode: FocusNode(),
|
||||
autofocus: true,
|
||||
onKeyEvent: (event) {
|
||||
if (event is KeyDownEvent &&
|
||||
event.logicalKey == LogicalKeyboardKey.escape) {
|
||||
debugPrint("Escape key pressed - hiding window.");
|
||||
windowManager.hide();
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Journaler'),
|
||||
actions: const [
|
||||
// Remove all buttons
|
||||
// No actions needed anymore
|
||||
],
|
||||
),
|
||||
appBar: AppBar(title: const Text('Journaler'), actions: const []),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Main journal area (8-10 columns)
|
||||
Expanded(
|
||||
flex:
|
||||
9, // Adjust flex factor (e.g., 8, 9, 10) for desired width ratio
|
||||
flex: 9,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Previous Entry (read-only conceptually, but TextField for easy display)
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _previousEntryController,
|
||||
maxLines: null, // Allows unlimited lines
|
||||
expands: true, // Fills the available space
|
||||
style:
|
||||
Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium, // Apply theme text style
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Previous Entry',
|
||||
// border: OutlineInputBorder(), // Handled by theme
|
||||
// contentPadding: EdgeInsets.all(8.0), // Handled by theme or default
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8), // Spacing
|
||||
// Current Entry
|
||||
const SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _currentEntryController,
|
||||
maxLines: null,
|
||||
expands: true,
|
||||
autofocus: true, // Focus here when window appears
|
||||
style:
|
||||
Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium, // Apply theme text style
|
||||
autofocus: true,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Current Entry (What\'s on your mind?)',
|
||||
// border: OutlineInputBorder(), // Handled by theme
|
||||
// contentPadding: EdgeInsets.all(8.0), // Handled by theme or default
|
||||
),
|
||||
onChanged: (text) {
|
||||
// Optional: Add auto-save logic here if desired
|
||||
},
|
||||
onChanged: (text) {},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8), // Spacing between columns
|
||||
// Todo List area (remaining columns)
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 3, // Adjust flex factor (12 - 9 = 3, or 12 - 8 = 4, etc.)
|
||||
flex: 3,
|
||||
child: TextField(
|
||||
controller: _todoController,
|
||||
maxLines: null,
|
||||
@@ -446,19 +319,3 @@ class _MainPageState extends State<MainPage> with WindowListener {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// REMOVED - Helper class for AppWindow interactions (optional, but good practice)
|
||||
// class AppWindow {
|
||||
// Future<void> init() async {
|
||||
// // Can add more window setup here if needed
|
||||
// }
|
||||
//
|
||||
// Future<void> show() async {
|
||||
// await windowManager.show();
|
||||
// await windowManager.focus();
|
||||
// }
|
||||
//
|
||||
// Future<void> hide() async {
|
||||
// await windowManager.hide();
|
||||
// }
|
||||
// }
|
||||
|
Reference in New Issue
Block a user