Rework UI code so selected routines can be called from different places
Mainly, there needs to be a way to always go back to the UI that called another, wherever that is.master
parent
6fe2a36a2a
commit
71494ebb38
|
|
@ -198,6 +198,8 @@ function update_status_widget(timer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// UI modes //
|
||||||
|
|
||||||
class TimerView {
|
class TimerView {
|
||||||
// Primary UI for displaying and operating a timer. The
|
// Primary UI for displaying and operating a timer. The
|
||||||
// PrimitiveTimer object is passed to the constructor as a
|
// PrimitiveTimer object is passed to the constructor as a
|
||||||
|
|
@ -472,14 +474,14 @@ class TimerView {
|
||||||
// Execute a UI action represented by the string `action`.
|
// Execute a UI action represented by the string `action`.
|
||||||
|
|
||||||
if (action === 'start/stop') {
|
if (action === 'start/stop') {
|
||||||
return this.start_stop_timer()
|
this.start_stop_timer()
|
||||||
|
|
||||||
} else if (action === 'edit_start') {
|
} else if (action === 'edit_start') {
|
||||||
// Display the timer start edit UI
|
switch_UI(
|
||||||
this.stop();
|
new TimerEditStart(
|
||||||
CURRENT_UI = new TimerViewMenu(this.timer);
|
this.timer,
|
||||||
CURRENT_UI.edit_start(
|
() => { switch_UI(new TimerView(this.timer)); }
|
||||||
() => { switch_UI(new TimerView(this.timer)); },
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -487,12 +489,19 @@ class TimerView {
|
||||||
|
|
||||||
|
|
||||||
class TimerFormatView {
|
class TimerFormatView {
|
||||||
// UI for selecting the display format of a timer. The
|
// UI for selecting the display format of a timer.
|
||||||
// PrimitiveTimer object is passed to the constructor as a
|
|
||||||
// parameter.
|
constructor(timer, back) {
|
||||||
|
// `timer` is the current PrimitiveTimer object being edited.
|
||||||
|
// `back` is the function that activates the previous UI to return
|
||||||
|
// to when the format selection is exited. It is passed `true` if
|
||||||
|
// the format change was confirmed and `false` if it was canceled.
|
||||||
|
// If `back` is not specified, a default back handler is used that
|
||||||
|
// returns to the TimerView if accepted or TimerViewMenu if
|
||||||
|
// canceled.
|
||||||
|
|
||||||
constructor(timer) {
|
|
||||||
this.timer = timer;
|
this.timer = timer;
|
||||||
|
this.back = back || this._back;
|
||||||
|
|
||||||
this.layout = null;
|
this.layout = null;
|
||||||
this.listeners = {};
|
this.listeners = {};
|
||||||
|
|
@ -509,6 +518,15 @@ class TimerFormatView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_back(ok) {
|
||||||
|
// Default back handler
|
||||||
|
if (ok) {
|
||||||
|
switch_UI(new TimerView(this.timer));
|
||||||
|
} else {
|
||||||
|
switch_UI(new TimerViewMenu(this.timer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
// Initialize, display, and activate the UI
|
// Initialize, display, and activate the UI
|
||||||
|
|
||||||
|
|
@ -743,28 +761,59 @@ class TimerFormatView {
|
||||||
tt.SETTINGS.format[row_id] = FORMAT_MENU[this.format_idx[row_id]];
|
tt.SETTINGS.format[row_id] = FORMAT_MENU[this.format_idx[row_id]];
|
||||||
}
|
}
|
||||||
tt.set_settings_dirty();
|
tt.set_settings_dirty();
|
||||||
switch_UI(new TimerView(this.timer));
|
this.back(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
// Return to TimerViewMenu without saving changes
|
// Return to TimerViewMenu without saving changes
|
||||||
switch_UI(new TimerViewMenu(this.timer));
|
this.back(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TimerViewMenu {
|
class TimerViewMenu {
|
||||||
// UI for displaying the timer menu. The PrimitiveTimer object is
|
// UI for displaying the timer menu.
|
||||||
// passed to the constructor as a parameter.
|
|
||||||
|
constructor(timer, back) {
|
||||||
|
// `timer` is the PrimitiveTimer object whose menu is being
|
||||||
|
// displayed. `back` is a function that activates the previous UI
|
||||||
|
// to return to when the menu is exited.
|
||||||
|
|
||||||
constructor(timer) {
|
|
||||||
this.timer = timer;
|
this.timer = timer;
|
||||||
|
this.back = back || this._back;
|
||||||
|
}
|
||||||
|
|
||||||
|
_back() {
|
||||||
|
// Default back handler
|
||||||
|
// Return to TimerView for the current timer
|
||||||
|
switch_UI(new TimerView(this.timer));
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
// Display and activate the top menu of the timer view menu.
|
// Display and activate the timer view menu.
|
||||||
|
|
||||||
this.top_menu();
|
const menu = {
|
||||||
|
'': {
|
||||||
|
title: this.timer.display_name(),
|
||||||
|
back: (() => { this.back(); }),
|
||||||
|
},
|
||||||
|
'Reset': () => { switch_UI(new ResetTimer(this.timer)); },
|
||||||
|
'Timers': () => { switch_UI(new TimerMenu(tt.TIMERS, this.timer)); },
|
||||||
|
'Edit': () => { switch_UI(new TimerEditMenu(this.timer)); },
|
||||||
|
'Format': () => { switch_UI(new TimerFormatView(this.timer)); },
|
||||||
|
'Add': () => {
|
||||||
|
tt.set_timers_dirty();
|
||||||
|
const new_timer = tt.add_timer(tt.TIMERS, this.timer);
|
||||||
|
switch_UI(new TimerEditMenu(new_timer));
|
||||||
|
},
|
||||||
|
'Delete': () => { switch_UI(new DeleteTimer(this.timer)); },
|
||||||
|
};
|
||||||
|
if (tt.TIMERS.length <= 1) {
|
||||||
|
// Prevent user deleting last timer
|
||||||
|
delete menu.Delete;
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
|
|
@ -773,85 +822,143 @@ class TimerViewMenu {
|
||||||
E.showMenu();
|
E.showMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
back() {
|
}
|
||||||
// Return to the timer view
|
|
||||||
// (i.e. the timer that was previously displayed)
|
|
||||||
|
|
||||||
|
class ResetTimer {
|
||||||
|
// UI for resetting a timer.
|
||||||
|
|
||||||
|
constructor(timer, back) {
|
||||||
|
// `timer` is the PrimitiveTimer object to reset.
|
||||||
|
// `back` is a function that activates the previous UI to return
|
||||||
|
// to when the menu is exited. It is passed `true` if the timer is
|
||||||
|
// reset and `false` if it is canceled. If `back` is not
|
||||||
|
// specified, a default back handler is used that returns to
|
||||||
|
// TimerView if accepted or TimerViewMenu if canceled.
|
||||||
|
|
||||||
|
this.timer = timer;
|
||||||
|
this.back = back || this._back;
|
||||||
|
}
|
||||||
|
|
||||||
|
_back(ok) {
|
||||||
|
// Default back handler
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
switch_UI(new TimerView(this.timer));
|
switch_UI(new TimerView(this.timer));
|
||||||
|
} else {
|
||||||
|
switch_UI(new TimerViewMenu(this.timer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
top_menu(back) {
|
start() {
|
||||||
// Display the top-level menu for the timer
|
// Display and activate the reset timer confirmation menu.
|
||||||
// `back` is optional and specifies the previous routine to return to
|
|
||||||
console.log('top_menu: ' + back);
|
|
||||||
|
|
||||||
const top_menu = {
|
const menu = {
|
||||||
'': {
|
|
||||||
title: this.timer.display_name(),
|
|
||||||
back: back || (() => { this.back(); }),
|
|
||||||
},
|
|
||||||
'Reset': () => { E.showMenu(reset_menu); },
|
|
||||||
'Timers': () => {
|
|
||||||
switch_UI(new TimerMenu(tt.TIMERS, this.timer));
|
|
||||||
},
|
|
||||||
'Edit': () => { this.edit_menu() },
|
|
||||||
'Format': () => {
|
|
||||||
switch_UI(new TimerFormatView(this.timer));
|
|
||||||
},
|
|
||||||
'Add': () => {
|
|
||||||
tt.set_timers_dirty();
|
|
||||||
const new_timer = tt.add_timer(tt.TIMERS, this.timer);
|
|
||||||
const timer_view_menu = new TimerViewMenu(new_timer);
|
|
||||||
timer_view_menu.edit_menu();
|
|
||||||
},
|
|
||||||
'Delete': () => { E.showMenu(delete_menu); },
|
|
||||||
};
|
|
||||||
if (tt.TIMERS.length <= 1) {
|
|
||||||
// Prevent user deleting last timer
|
|
||||||
delete top_menu.Delete;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reset_menu = {
|
|
||||||
'': {
|
'': {
|
||||||
title: 'Confirm reset',
|
title: 'Confirm reset',
|
||||||
back: () => { E.showMenu(top_menu); }
|
back: () => { this.back(false); }
|
||||||
},
|
},
|
||||||
'Reset': () => {
|
'Reset': () => {
|
||||||
this.timer.reset();
|
this.timer.reset();
|
||||||
tt.set_timers_dirty();
|
tt.set_timers_dirty();
|
||||||
this.back();
|
this.back(true);
|
||||||
},
|
},
|
||||||
'Cancel': () => { E.showMenu(top_menu); },
|
'Cancel': () => { this.back(false); },
|
||||||
};
|
};
|
||||||
|
|
||||||
const delete_menu = {
|
E.showMenu(menu);
|
||||||
'': {
|
|
||||||
title: 'Confirm delete',
|
|
||||||
back: () => { E.showMenu(top_menu); }
|
|
||||||
},
|
|
||||||
'Delete': () => {
|
|
||||||
tt.set_timers_dirty();
|
|
||||||
switch_UI(new TimerView(tt.delete_timer(tt.TIMERS, this.timer)));
|
|
||||||
},
|
|
||||||
'Cancel': () => { E.showMenu(top_menu); },
|
|
||||||
};
|
|
||||||
|
|
||||||
E.showMenu(top_menu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
edit_menu(back) {
|
stop() {
|
||||||
// Display the edit menu for the timer. This can be called in
|
// Shut down the UI and clean up listeners and handlers
|
||||||
// place of `start` to jump directly to the edit menu.
|
|
||||||
// `back` is optional and specifies the previous routine to return to
|
E.showMenu();
|
||||||
console.log('edit_menu: ' + back);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeleteTimer {
|
||||||
|
// UI for deleting a timer.
|
||||||
|
|
||||||
|
constructor(timer, back) {
|
||||||
|
// `timer` is the PrimitiveTimer object to delete. `back` is a
|
||||||
|
// function that activates the previous UI to return to when the
|
||||||
|
// menu is exited. It is passed `true` for the first parameter if
|
||||||
|
// the timer is deleted and `false` if it is canceled. For the
|
||||||
|
// second parameter, it is passed the same timer if canceled or
|
||||||
|
// another existing timer in the list if the given timer was
|
||||||
|
// deleted. If `back` is not specified, a default back handler is
|
||||||
|
// used that returns to TimerView if accepted or TimerViewMenu if
|
||||||
|
// canceled.
|
||||||
|
|
||||||
|
this.timer = timer;
|
||||||
|
this.back = back || this._back;
|
||||||
|
}
|
||||||
|
|
||||||
|
_back(ok, timer) {
|
||||||
|
// Default back handler
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
switch_UI(new TimerView(timer));
|
||||||
|
} else {
|
||||||
|
switch_UI(new TimerViewMenu(timer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
// Display and activate the delete timer confirmation menu.
|
||||||
|
|
||||||
|
const menu = {
|
||||||
|
'': {
|
||||||
|
title: 'Confirm delete',
|
||||||
|
back: () => { this.back(false, this.timer); }
|
||||||
|
},
|
||||||
|
'Delete': () => {
|
||||||
|
ok = true;
|
||||||
|
tt.set_timers_dirty();
|
||||||
|
this.back(true, tt.delete_timer(tt.TIMERS, this.timer));
|
||||||
|
},
|
||||||
|
'Cancel': () => { this.back(false, this.timer) },
|
||||||
|
};
|
||||||
|
|
||||||
|
E.showMenu(menu);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
// Shut down the UI and clean up listeners and handlers
|
||||||
|
|
||||||
|
E.showMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TimerEditMenu {
|
||||||
|
// UI for editing a timer.
|
||||||
|
|
||||||
|
constructor(timer, back) {
|
||||||
|
// `timer` is the PrimitiveTimer object to edit. `back` is a
|
||||||
|
// function that activates the previous UI to return to when the
|
||||||
|
// menu is exited. If `back` is not specified, a default back
|
||||||
|
// handler is used that returns to TimerViewMenu.
|
||||||
|
|
||||||
|
this.timer = timer;
|
||||||
|
this.back = back || this._back;
|
||||||
|
}
|
||||||
|
|
||||||
|
_back() {
|
||||||
|
// Default back handler
|
||||||
|
|
||||||
|
switch_UI(new TimerViewMenu(this.timer));
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
// Display the edit menu for the timer.
|
||||||
|
|
||||||
let keyboard = null;
|
let keyboard = null;
|
||||||
try { keyboard = require("textinput"); } catch (e) {}
|
try { keyboard = require("textinput"); } catch (e) {}
|
||||||
|
|
||||||
const edit_menu = {
|
const menu = {
|
||||||
'': {
|
'': {
|
||||||
title: 'Edit: ' + this.timer.display_name(),
|
title: 'Edit: ' + this.timer.display_name(),
|
||||||
back: back || (() => { this.top_menu() }),
|
back: () => { this.back(); }
|
||||||
},
|
},
|
||||||
'Name': {
|
'Name': {
|
||||||
value: this.timer.name,
|
value: this.timer.name,
|
||||||
|
|
@ -860,12 +967,12 @@ class TimerViewMenu {
|
||||||
keyboard.input({text:this.timer.name}).then(text => {
|
keyboard.input({text:this.timer.name}).then(text => {
|
||||||
this.timer.name = text;
|
this.timer.name = text;
|
||||||
tt.set_timers_dirty();
|
tt.set_timers_dirty();
|
||||||
setTimeout(() => { this.edit_menu(); }, 0);
|
switch_UI(new TimerViewMenu(this.timer));
|
||||||
});
|
});
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Start': () => { this.edit_start(); },
|
'Start': () => { switch_UI(new TimerEditStart(this.timer)); },
|
||||||
'At end': {
|
'At end': {
|
||||||
// Option to auto-start another timer when this one ends
|
// Option to auto-start another timer when this one ends
|
||||||
format: v => v === -1
|
format: v => v === -1
|
||||||
|
|
@ -899,17 +1006,45 @@ class TimerViewMenu {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!keyboard) {
|
if (!keyboard) {
|
||||||
delete edit_menu.Name;
|
// Hide the Name menu item if text input module is not available
|
||||||
|
delete menu.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
E.showMenu(edit_menu);
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
edit_start(back) {
|
stop() {
|
||||||
|
// Shut down the UI and clean up listeners and handlers
|
||||||
|
|
||||||
|
E.showMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TimerEditStart {
|
||||||
|
// UI for editing the timer's starting value.
|
||||||
|
|
||||||
|
constructor(timer, back) {
|
||||||
|
// `timer` is the PrimitiveTimer object to edit. `back` is a
|
||||||
|
// function that activates the previous UI to return to when the
|
||||||
|
// menu is exited. It is passed `true` if the timer is edited and
|
||||||
|
// `false` if it is canceled. If `back` is not specified, a
|
||||||
|
// default back handler is used that returns to TimerView.
|
||||||
|
|
||||||
|
this.timer = timer;
|
||||||
|
this.back = back || this._back;
|
||||||
|
}
|
||||||
|
|
||||||
|
_back(ok) {
|
||||||
|
// Default back handler
|
||||||
|
|
||||||
|
switch_UI(new TimerEditMenu(this.timer));
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
// Display the edit > start menu for the timer
|
// Display the edit > start menu for the timer
|
||||||
// (i.e. the timer's starting value)
|
|
||||||
// `back` is optional and specifies the previous routine to return to
|
var ok = false;
|
||||||
console.log('edit_start: ' + back);
|
|
||||||
|
|
||||||
let origin_hms = {
|
let origin_hms = {
|
||||||
h: Math.floor(this.timer.origin / 3600),
|
h: Math.floor(this.timer.origin / 3600),
|
||||||
|
|
@ -940,60 +1075,72 @@ class TimerViewMenu {
|
||||||
wrap_3: true,
|
wrap_3: true,
|
||||||
separator_1: ':',
|
separator_1: ':',
|
||||||
separator_2: ':',
|
separator_2: ':',
|
||||||
back: back || (() => { this.edit_menu(); }),
|
back: () => { this.back(ok); },
|
||||||
onchange: (h, m, s) => {
|
onchange: (h, m, s) => {
|
||||||
|
ok = true;
|
||||||
this.timer.origin = h * 3600 + m * 60 + s;
|
this.timer.origin = h * 3600 + m * 60 + s;
|
||||||
tt.set_timers_dirty();
|
tt.set_timers_dirty();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class TimerMenu {
|
|
||||||
// UI for displaying the list of timers. The list of
|
|
||||||
// PrimitiveTimer objects is passed to the constructor as a
|
|
||||||
// parameter. The currently focused timer is passed as the
|
|
||||||
// second parameter.
|
|
||||||
|
|
||||||
constructor(timers, focused_timer) {
|
|
||||||
this.timers = timers;
|
|
||||||
this.focused_timer = focused_timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
// Display the top timer menu
|
|
||||||
|
|
||||||
this.top_menu();
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
// Shut down the UI and clean up listeners and handlers
|
// Shut down the UI and clean up listeners and handlers
|
||||||
|
|
||||||
E.showMenu();
|
E.showMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
back() {
|
|
||||||
// Return to the timer's menu
|
|
||||||
|
|
||||||
switch_UI(new TimerViewMenu(this.focused_timer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
top_menu() {
|
|
||||||
// Display the top-level menu for the timer list
|
class TimerMenu {
|
||||||
|
// UI for choosing among the list of defined timers.
|
||||||
|
|
||||||
|
constructor(timers, focused_timer, back) {
|
||||||
|
// `timers` is the list of PrimitiveTimer objects to display.
|
||||||
|
// `focused_timer` is the PrimitiveTimer object that is currently
|
||||||
|
// being displayed. `back` is a function that activates the
|
||||||
|
// previous UI to return to when the menu is exited. It is passed
|
||||||
|
// the selected timer object if a timer is selected or `null` if
|
||||||
|
// the menu is canceled, and the last-focused timer object. If not
|
||||||
|
// specified, a default back handler is used that returns to
|
||||||
|
// TimerView for the selected timer or TimerViewMenu if canceled.
|
||||||
|
|
||||||
|
this.timers = timers;
|
||||||
|
this.focused_timer = focused_timer;
|
||||||
|
this.back = back || this._back;
|
||||||
|
}
|
||||||
|
|
||||||
|
_back(timer, focused_timer) {
|
||||||
|
// Default back handler
|
||||||
|
|
||||||
|
if (timer) {
|
||||||
|
switch_UI(new TimerView(timer));
|
||||||
|
} else {
|
||||||
|
switch_UI(new TimerViewMenu(focused_timer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
// Display the timer menu
|
||||||
|
|
||||||
let menu = {
|
let menu = {
|
||||||
'': {
|
'': {
|
||||||
title: "Timers",
|
title: "Timers",
|
||||||
back: this.back.bind(this)
|
back: () => { this.back(null, this.focused_timer); }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.timers.forEach((timer) => {
|
this.timers.forEach((timer) => {
|
||||||
menu[timer.display_status() + ' ' + timer.display_name()] =
|
menu[timer.display_status() + ' ' + timer.display_name()] =
|
||||||
() => { switch_UI(new TimerView(timer)); };
|
() => { this.back(timer, this.focused_timer); };
|
||||||
});
|
});
|
||||||
E.showMenu(menu);
|
E.showMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
// Shut down the UI and clean up listeners and handlers
|
||||||
|
|
||||||
|
E.showMenu();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue