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 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(); } } }