mirror of
https://github.com/oleid/gnome-shell-teatime.git
synced 2022-04-29 18:53:50 +00:00
Merge pull request #2 from tommie-lie/feature/configurable-times
Feature: configurable steeping times
This commit is contained in:
commit
47f9d6195c
55
extension.js
55
extension.js
@ -25,14 +25,8 @@ const N_ = function(e) { return e; };
|
|||||||
|
|
||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = ExtensionUtils.getCurrentExtension();
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Utils = Me.imports.utils;
|
||||||
|
|
||||||
// TODO: make this configurable via gsettings
|
|
||||||
const defaultTeaList = [
|
|
||||||
{name : "Green tea", time : 180 },
|
|
||||||
{name : "Black tea", time : 210 },
|
|
||||||
{name : "Fruit tea", time : 7 * 60},
|
|
||||||
{name : "White tea", time : 120}
|
|
||||||
];
|
|
||||||
|
|
||||||
const TeaTime = new Lang.Class({
|
const TeaTime = new Lang.Class({
|
||||||
Name : 'TeaTime',
|
Name : 'TeaTime',
|
||||||
@ -41,13 +35,14 @@ const TeaTime = new Lang.Class({
|
|||||||
_init : function() {
|
_init : function() {
|
||||||
this.parent(0.0, "TeaTime");
|
this.parent(0.0, "TeaTime");
|
||||||
|
|
||||||
|
this._settings = Utils.getSettings();
|
||||||
|
|
||||||
this._logo = new St.Icon({
|
this._logo = new St.Icon({
|
||||||
icon_name : 'utilities-teatime',
|
icon_name : 'utilities-teatime',
|
||||||
style_class : 'system-status-icon'
|
style_class : 'system-status-icon'
|
||||||
});
|
});
|
||||||
|
|
||||||
// set timer widget
|
// set timer widget
|
||||||
|
|
||||||
this._timer = new St.DrawingArea({
|
this._timer = new St.DrawingArea({
|
||||||
reactive : true
|
reactive : true
|
||||||
});
|
});
|
||||||
@ -62,40 +57,26 @@ const TeaTime = new Lang.Class({
|
|||||||
|
|
||||||
this._createMenu();
|
this._createMenu();
|
||||||
},
|
},
|
||||||
|
|
||||||
_createMenu : function() {
|
_createMenu : function() {
|
||||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
this._addTeaList();
|
this._settings.connect("changed::" + Utils.TEATIME_STEEP_TIMES_KEY,
|
||||||
|
Lang.bind(this, this._updateTeaList));
|
||||||
|
this._updateTeaList();
|
||||||
},
|
},
|
||||||
_formatTime : function(seconds) {
|
_updateTeaList : function(config, output) {
|
||||||
let a = new Date(0,0,0); // important: hour needs to be set to zero in _locale_ time
|
// make sure the menu is empty
|
||||||
|
this.menu.removeAll();
|
||||||
|
|
||||||
a.setTime(a.getTime()+ seconds * 1000); // set time in msec, adding the time we want
|
// fill with new teas
|
||||||
|
let list = this._settings.get_value(Utils.TEATIME_STEEP_TIMES_KEY).unpack();
|
||||||
|
for (let teaname in list) {
|
||||||
|
let time = list[teaname].get_uint32();
|
||||||
|
|
||||||
if (seconds > 3600)
|
let menuItem = new PopupMenu.PopupMenuItem(teaname + ": " + Utils.formatTime(time));
|
||||||
return a.toLocaleFormat("%H:%M:%S");
|
menuItem.connect('activate', Lang.bind(this, function() {
|
||||||
else
|
this._initCountdown(time);
|
||||||
return a.toLocaleFormat("%M:%S");
|
}));
|
||||||
},
|
this.menu.addMenuItem(menuItem);
|
||||||
_addTeaList : function(config, output) {
|
|
||||||
let item = new PopupMenu.PopupMenuItem(_("brewing times"));
|
|
||||||
item.label.add_style_class_name('display-subtitle');
|
|
||||||
item.actor.reactive = false;
|
|
||||||
item.actor.can_focus = false;
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._callbacks = [];
|
|
||||||
|
|
||||||
defaultTeaList.sort(function(a, b) {
|
|
||||||
return -1 * (a.time < b.time) + (a.time > b.time);
|
|
||||||
});
|
|
||||||
|
|
||||||
for ( var i = 0; i < defaultTeaList.length; i++) {
|
|
||||||
let tea = defaultTeaList[i];
|
|
||||||
let item = new PopupMenu.PopupMenuItem(this._formatTime(tea.time) + " - " + tea.name);
|
|
||||||
|
|
||||||
this._callbacks.push( function() {this._initCountdown(tea.time); });
|
|
||||||
item.connect('activate', Lang.bind(this, this._callbacks[i]));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_showNotification : function(subject, text) {
|
_showNotification : function(subject, text) {
|
||||||
|
@ -1 +1 @@
|
|||||||
{"shell-version": ["3.6"], "uuid": "TeaTime@oleid.mescharet.de", "name": "TeaTime", "description": "A tea brewing timer"}
|
{"shell-version": ["3.6"], "uuid": "TeaTime@oleid.mescharet.de", "name": "TeaTime", "settings-schema": "org.gnome.shell.extensions.teatime", "description": "A tea brewing timer"}
|
||||||
|
185
prefs.js
Normal file
185
prefs.js
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
/* Olaf Leidinger <oleid@mescharet.de>
|
||||||
|
Thomas Liebetraut <thomas@tommie-lie.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Gdk = imports.gi.Gdk;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||||
|
const Mainloop = imports.mainloop; // timer
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
|
|
||||||
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
||||||
|
const _ = Gettext.gettext;
|
||||||
|
const N_ = function(e) { return e; };
|
||||||
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
const Utils = Me.imports.utils;
|
||||||
|
|
||||||
|
|
||||||
|
const Columns = {
|
||||||
|
TEA_NAME: 0,
|
||||||
|
STEEP_TIME: 1,
|
||||||
|
ADJUSTMENT: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
const TeaTimePrefsWidget = new Lang.Class({
|
||||||
|
Name : 'TeaTimePrefsWidget',
|
||||||
|
Extends : Gtk.Box,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent({ orientation: Gtk.Orientation.VERTICAL });
|
||||||
|
|
||||||
|
this._tealist = new Gtk.ListStore();
|
||||||
|
this._tealist.set_column_types([
|
||||||
|
GObject.TYPE_STRING,
|
||||||
|
GObject.TYPE_INT,
|
||||||
|
Gtk.Adjustment
|
||||||
|
]);
|
||||||
|
|
||||||
|
this._settings = Utils.getSettings();
|
||||||
|
this._inhibitUpdate = true;
|
||||||
|
this._settings.connect("changed::" + Utils.TEATIME_STEEP_TIMES_KEY,
|
||||||
|
Lang.bind(this, this._refresh));
|
||||||
|
|
||||||
|
this._initWindow();
|
||||||
|
this.vexpand = true;
|
||||||
|
this._inhibitUpdate = false;
|
||||||
|
this._refresh();
|
||||||
|
this._tealist.connect("row-changed", Lang.bind(this, this._save));
|
||||||
|
this._tealist.connect("row-deleted", Lang.bind(this, this._save));
|
||||||
|
},
|
||||||
|
_initWindow: function() {
|
||||||
|
this.treeview = new Gtk.TreeView({model: this._tealist, expand: true});
|
||||||
|
this.treeview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
|
||||||
|
this.add(this.treeview);
|
||||||
|
|
||||||
|
let teaname = new Gtk.TreeViewColumn({ title: _("Tea"), expand: true });
|
||||||
|
let renderer = new Gtk.CellRendererText({ editable: true });
|
||||||
|
// When the renderer is done editing it's value, we first write
|
||||||
|
// the new value to the view's model, i.e. this._tealist.
|
||||||
|
// This makes life a little harder due to chaining of callbacks
|
||||||
|
// and the need for this._inhibitUpdate, but it feels a lot cleaner
|
||||||
|
// when the UI does not know about the config storage backend.
|
||||||
|
renderer.connect("edited", Lang.bind(this, function(renderer, pathString, newValue) {
|
||||||
|
let [store, iter] = this._tealist.get_iter(Gtk.TreePath.new_from_string(pathString));
|
||||||
|
this._tealist.set(iter, [Columns.TEA_NAME], [newValue]);
|
||||||
|
}));
|
||||||
|
teaname.pack_start(renderer, true);
|
||||||
|
teaname.add_attribute(renderer, "text", Columns.TEA_NAME);
|
||||||
|
this.treeview.append_column(teaname);
|
||||||
|
|
||||||
|
let steeptime = new Gtk.TreeViewColumn({ title: _("Steep time"), min_width: 150 });
|
||||||
|
let spinrenderer = new Gtk.CellRendererSpin({ editable: true });
|
||||||
|
// See comment above.
|
||||||
|
spinrenderer.connect("edited", Lang.bind(this, function(renderer, pathString, newValue) {
|
||||||
|
let [store, iter] = this._tealist.get_iter(Gtk.TreePath.new_from_string(pathString));
|
||||||
|
this._tealist.set(iter, [Columns.STEEP_TIME], [parseInt(newValue)]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
steeptime.pack_start(spinrenderer, true);
|
||||||
|
steeptime.add_attribute(spinrenderer, "adjustment", Columns.ADJUSTMENT);
|
||||||
|
steeptime.add_attribute(spinrenderer, "text", Columns.STEEP_TIME);
|
||||||
|
this.treeview.append_column(steeptime);
|
||||||
|
|
||||||
|
|
||||||
|
this.toolbar = new Gtk.Toolbar({ icon_size: 1 });
|
||||||
|
this.toolbar.get_style_context().add_class("inline-toolbar");
|
||||||
|
this.add(this.toolbar);
|
||||||
|
this.addButton = new Gtk.ToolButton({ icon_name: "list-add-symbolic", use_action_appearance: false });
|
||||||
|
this.addButton.connect("clicked", Lang.bind(this, this._addTea));
|
||||||
|
this.toolbar.insert(this.addButton, -1);
|
||||||
|
this.removeButton = new Gtk.ToolButton({ icon_name: "list-remove-symbolic", use_action_appearance: false });
|
||||||
|
this.removeButton.connect("clicked", Lang.bind(this, this._removeSelectedTea));
|
||||||
|
this.toolbar.insert(this.removeButton, -1);
|
||||||
|
},
|
||||||
|
_refresh: function() {
|
||||||
|
// don't update the model if someone else is messing with the backend
|
||||||
|
if (this._inhibitUpdate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let list = this._settings.get_value(Utils.TEATIME_STEEP_TIMES_KEY).unpack();
|
||||||
|
|
||||||
|
// stop everyone from reacting to the changes we are about to produce
|
||||||
|
// in the model
|
||||||
|
this._inhibitUpdate = true;
|
||||||
|
|
||||||
|
this._tealist.clear();
|
||||||
|
for (let teaname in list) {
|
||||||
|
let time = list[teaname].get_uint32();
|
||||||
|
|
||||||
|
let adj = new Gtk.Adjustment({ lower: 1, step_increment: 1, upper: 65535, value: time });
|
||||||
|
this._tealist.set(this._tealist.append(),
|
||||||
|
[Columns.TEA_NAME, Columns.STEEP_TIME, Columns.ADJUSTMENT],
|
||||||
|
[teaname, time, adj]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._inhibitUpdate = false;
|
||||||
|
},
|
||||||
|
_addTea: function() {
|
||||||
|
let adj = new Gtk.Adjustment({ lower: 1, step_increment: 1, upper: 65535, value: 1 });
|
||||||
|
let item = this._tealist.append();
|
||||||
|
this._tealist.set(item,
|
||||||
|
[Columns.TEA_NAME, Columns.STEEP_TIME, Columns.ADJUSTMENT],
|
||||||
|
["", 1, adj]);
|
||||||
|
this.treeview.set_cursor(this._tealist.get_path(item),
|
||||||
|
this.treeview.get_column(Columns.TEA_NAME),
|
||||||
|
true);
|
||||||
|
},
|
||||||
|
_removeSelectedTea: function() {
|
||||||
|
let [selection, store] = this.treeview.get_selection().get_selected_rows();
|
||||||
|
let iters = [];
|
||||||
|
for (let i = 0; i < selection.length; ++i) {
|
||||||
|
let [isSet, iter] = store.get_iter(selection[i]);
|
||||||
|
if (isSet) {
|
||||||
|
iters.push(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// it's ok not to inhibit updates here as remove != change
|
||||||
|
iters.forEach(function(value, index, array) {
|
||||||
|
store.remove(value) }
|
||||||
|
);
|
||||||
|
|
||||||
|
this.treeview.get_selection().unselect_all();
|
||||||
|
},
|
||||||
|
_save: function(store, path_, iter_) {
|
||||||
|
// don't update the backend if someone else is messing with the model
|
||||||
|
if (this._inhibitUpdate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let values = [];
|
||||||
|
this._tealist.foreach(function(store, path, iter) {
|
||||||
|
values.push(GLib.Variant.new_dict_entry(
|
||||||
|
GLib.Variant.new_string(store.get_value(iter, Columns.TEA_NAME)),
|
||||||
|
GLib.Variant.new_uint32(store.get_value(iter, Columns.STEEP_TIME))))
|
||||||
|
});
|
||||||
|
let settingsValue = GLib.Variant.new_array(GLib.VariantType.new("{su}"), values);
|
||||||
|
|
||||||
|
// all changes have happened through the UI, we can safely
|
||||||
|
// disable updating it here to avoid an infinite loop
|
||||||
|
this._inhibitUpdate = true;
|
||||||
|
|
||||||
|
this._settings.set_value(Utils.TEATIME_STEEP_TIMES_KEY, settingsValue);
|
||||||
|
|
||||||
|
this._inhibitUpdate = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPrefsWidget() {
|
||||||
|
let widget = new TeaTimePrefsWidget();
|
||||||
|
|
||||||
|
widget.show_all();
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
12
schemas/org.gnome.shell.extensions.teatime.gschema.xml
Normal file
12
schemas/org.gnome.shell.extensions.teatime.gschema.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<schemalist>
|
||||||
|
<schema id="org.gnome.shell.extensions.teatime" path="/org/gnome/shell/extensions/teatime/">
|
||||||
|
<key name="steep-times" type="a{su}">
|
||||||
|
<default>{ "Green tea": 180, "Black tea": 210, "Fruit tea": 420, "White tea": 120 }</default>
|
||||||
|
<summary>Tea drawing times list</summary>
|
||||||
|
<description>A mapping of a tea times to their corresponding drawing time in seconds.</description>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
|
</schemalist>
|
||||||
|
|
||||||
|
|
53
utils.js
Normal file
53
utils.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
/* Olaf Leidinger <oleid@mescharet.de>
|
||||||
|
Thomas Liebetraut <thomas@tommie-lie.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
|
|
||||||
|
const TEATIME_STEEP_TIMES_KEY = 'steep-times';
|
||||||
|
|
||||||
|
function getSettings(schema) {
|
||||||
|
let extension = ExtensionUtils.getCurrentExtension();
|
||||||
|
|
||||||
|
schema = schema || extension.metadata['settings-schema'];
|
||||||
|
|
||||||
|
const GioSSS = Gio.SettingsSchemaSource;
|
||||||
|
|
||||||
|
// check if this extension was built with "make zip-file", and thus
|
||||||
|
// has the schema files in a subfolder
|
||||||
|
// otherwise assume that extension has been installed in the
|
||||||
|
// same prefix as gnome-shell (and therefore schemas are available
|
||||||
|
// in the standard folders)
|
||||||
|
let schemaDir = extension.dir.get_child('schemas');
|
||||||
|
let schemaSource;
|
||||||
|
if (schemaDir.query_exists(null)) {
|
||||||
|
schemaSource = GioSSS.new_from_directory(schemaDir.get_path(),
|
||||||
|
GioSSS.get_default(),
|
||||||
|
false);
|
||||||
|
} else {
|
||||||
|
schemaSource = GioSSS.get_default();
|
||||||
|
}
|
||||||
|
|
||||||
|
let schemaObj = schemaSource.lookup(schema, true);
|
||||||
|
if (!schemaObj)
|
||||||
|
throw new Error('Schema ' + schema + ' could not be found for extension '
|
||||||
|
+ extension.metadata.uuid + '. Please check your installation.');
|
||||||
|
|
||||||
|
return new Gio.Settings({ settings_schema: schemaObj });
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(seconds) {
|
||||||
|
let a = new Date(0,0,0); // important: hour needs to be set to zero in _locale_ time
|
||||||
|
|
||||||
|
a.setTime(a.getTime()+ seconds * 1000); // set time in msec, adding the time we want
|
||||||
|
|
||||||
|
if (seconds > 3600)
|
||||||
|
return a.toLocaleFormat("%H:%M:%S");
|
||||||
|
else
|
||||||
|
return a.toLocaleFormat("%M:%S");
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user