ConTime/view/MainForm.cs

146 lines
6.8 KiB
C#
Raw Permalink Normal View History

2025-10-12 21:47:06 +00:00
using Contime.model;
namespace Contime.view {
public partial class MainForm : Form {
// --- Events for the Controller ---
public event Action WorkdayStartStopClicked;
public event Action TaskStartStopClicked;
public event Action GoOutOfContextClicked;
public event Action ToggleTaskStatusClicked;
public event Action AddTaskClicked;
public event Action<long> TaskSelected;
public event Action SaveContextClicked;
public event Action ExportClicked;
// --- Properties for the Controller ---
public string NewTaskName => txtNewTask.Text;
public string ContextText { get => txtContext.Text; set => txtContext.Text = value; }
public MainForm() {
InitializeComponent();
}
// Handles full UI redraws, especially for the task list and summary data
public void UpdateView(TimeTrackerModel model) {
this.SuspendLayout();
UpdateTaskList(model);
UpdateSummaryStats(model);
UpdateTimersAndState(model); // Also call the lighter update to ensure consistency
this.ResumeLayout();
}
// Handles high-frequency updates (timers, button states)
public void UpdateTimersAndState(TimeTrackerModel model) {
lblWorkdayTimer.Text = model.WorkdayTime.ToString(@"hh\:mm\:ss");
lblTaskTimer.Text = model.ActiveTaskTime.ToString(@"hh\:mm\:ss");
lblSelectedTaskTotalTime.Text = model.SelectedTaskTotalTime.ToString(@"hh\:mm\:ss");
lblStatus.Text = GetStatusText(model.CurrentState);
lblStatus.ForeColor = GetStatusColor(model.CurrentState);
lblSelectedTask.Text = model.SelectedTask?.Name ?? "None";
// --- Update Button States and Text ---
btnWorkdayStartStop.Text = (model.CurrentState == TimeTrackerModel.AppState.Idle) ? "Start Workday" : "Stop Workday";
btnWorkdayStartStop.BackColor = (model.CurrentState == TimeTrackerModel.AppState.Idle) ? Color.FromArgb(0, 122, 204) : Color.FromArgb(192, 0, 0);
btnGoOutOfContext.Enabled = model.CurrentState != TimeTrackerModel.AppState.Idle;
btnGoOutOfContext.Text = (model.CurrentState == TimeTrackerModel.AppState.OutOfContext) ? "Return to Work" : "Go Out of Context";
bool taskIsSelectedAndOpen = model.SelectedTask != null && model.SelectedTask.Status == "open";
bool canStartTask = model.CurrentState == TimeTrackerModel.AppState.WorkdayActive && taskIsSelectedAndOpen;
btnTaskStartStop.Enabled = canStartTask || model.CurrentState == TimeTrackerModel.AppState.TaskActive;
btnTaskStartStop.Text = (model.CurrentState == TimeTrackerModel.AppState.TaskActive) ? "Stop Task" : "Start Task";
btnTaskStartStop.BackColor = (model.CurrentState == TimeTrackerModel.AppState.TaskActive) ? Color.FromArgb(220, 53, 69) : Color.FromArgb(40, 167, 69);
btnToggleTaskStatus.Enabled = model.SelectedTask != null && model.CurrentState != TimeTrackerModel.AppState.TaskActive;
btnToggleTaskStatus.Text = model.SelectedTask?.Status == "open" ? "Mark as Finished" : "Re-open Task";
lstTasks.Enabled = model.CurrentState != TimeTrackerModel.AppState.TaskActive;
// Show or hide the 'Out of Context' input fields
bool outOfContext = model.CurrentState == TimeTrackerModel.AppState.OutOfContext;
txtContext.Visible = outOfContext;
btnSaveContext.Visible = outOfContext;
if (outOfContext) ContextText = model.OutOfContextReason;
}
private void UpdateSummaryStats(TimeTrackerModel model) {
lblDayTotal.Text = $"Today: {model.DayTotal:hh\\:mm\\:ss}";
lblWeekTotal.Text = $"This Week: {model.WeekTotal:hh\\:mm\\:ss}";
}
private void LstTasks_SelectedIndexChanged(object sender, EventArgs e) {
if (lstTasks.SelectedItems.Count > 0)
TaskSelected?.Invoke((long)lstTasks.SelectedItems[0].Tag);
else
TaskSelected?.Invoke(-1);
}
private void LstTasks_MouseDoubleClick(object sender, MouseEventArgs e) {
if (lstTasks.SelectedItems.Count == 1) {
var selectedItem = lstTasks.SelectedItems[0];
string taskName = selectedItem.SubItems[0].Text;
string totalTime = selectedItem.SubItems[1].Text;
string status = selectedItem.SubItems[2].Text;
string message = $"Task: {taskName}\nTotal Time: {totalTime}\nStatus: {status}";
MessageBox.Show(message, "Task Details", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
private void UpdateTaskList(TimeTrackerModel model) {
lstTasks.SelectedIndexChanged -= LstTasks_SelectedIndexChanged; // Unsubscribe to prevent feedback loop
long? selectedTaskId = model.SelectedTask?.Id;
lstTasks.Items.Clear();
foreach (var task in model.Tasks) {
var item = new ListViewItem(new[] { task.Name, task.ElapsedTime.ToString(@"hh\:mm\:ss"), task.Status }) {
Tag = task.Id,
ForeColor = task.Status == "closed" ? Color.Gray : Color.White
};
lstTasks.Items.Add(item);
if (task.Id == selectedTaskId) {
item.Selected = true;
}
}
lstTasks.SelectedIndexChanged += LstTasks_SelectedIndexChanged; // Re-subscribe
}
private string GetStatusText(TimeTrackerModel.AppState state) {
switch (state) {
case TimeTrackerModel.AppState.Idle: return "Idle";
case TimeTrackerModel.AppState.WorkdayActive: return "Workday Active";
case TimeTrackerModel.AppState.TaskActive: return "Task in Progress";
case TimeTrackerModel.AppState.OutOfContext: return "Out of Context";
default: return "Unknown";
}
}
private Color GetStatusColor(TimeTrackerModel.AppState state) {
switch (state) {
case TimeTrackerModel.AppState.Idle: return Color.White;
case TimeTrackerModel.AppState.WorkdayActive: return Color.LightSkyBlue;
case TimeTrackerModel.AppState.TaskActive: return Color.LightGreen;
case TimeTrackerModel.AppState.OutOfContext: return Color.Orange;
default: return Color.White;
}
}
public void ShowNotification(string title, string message) {
notifyIcon.BalloonTipTitle = title;
notifyIcon.BalloonTipText = message;
notifyIcon.ShowBalloonTip(3000);
}
private void BtnExport_Click(object sender, EventArgs e) {
ExportClicked?.Invoke();
}
}
}