Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions todo@NotSirius-A/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Themeable, customizable, and easy-to-use TODO desklet. Keep track of what you ne

### Customization

You can make this desklet fit your desktop style. Customize the colors, fonts, icons etc.
You can make this desklet fit your desktop style. Customize the colors, fonts, icons etc. There are plenty of settings to tinker with.


### Usage
Expand All @@ -25,12 +25,30 @@ You can add/edit tasks in desklet settings or by interacting with the desklet it
- Right-click to select multiple tasks or left-click on the edge (only relevant if using toolbar)
- Middle-click to remove a task

If you want to add more tasks, right-click anywhere on the desklet and select the `Add new task` option inside the popup menu or use the toolbar.
If you want to add more tasks, right-click anywhere on the desklet and select the `Add new task` option inside the popup menu or use the toolbar. If you need to write something using multiple lines of text just press Shift+Enter while typing, which will give you a new line.


### Tips

- You can add multiple lists by clicking the "+" icons in your desklet manager.
- You can backup your desklets by going to the settings. There you should find an icon in the top right corner. Select the `Export to a file` option or import to load your backup.
- You can add a transparent border to your tasks to make them look bigger.
- When you disable desklet decorations you can still grab the desklet by the invisible border around it.
- I did my best to accommodate users with many display/text scaling factors, but sometimes the desklet looks a bit different on some settings, so always adjust it to your liking. Make sure to restart cinnamon or reboot after changing the display scale.


### Input methods ibus/fcitx (Chinese, Japanese etc.)

If you are using alternate input methods please note that it is impossible to use them while editing text directly on your desktop. In such case please use the toolbar edit button or desklet settings.

I have spent many hours trying to make ibus/fcitx work while directly editing tasks on the desktop, but I was unable to find a solution. I am not sure a fix even exists, but I have found a workaround. I added the edit button on the toolbar, which opens an edit dialog, that is using a different technology (Gtk) to that of the desklet (Clutter/St). This other technology fortunately does work with ibus/fcitx.


### What can be improved?

* Improve font parsing.

* Fix the `clutter_focus` error.

* Add a way to mark tasks as high priority.
* Somehow add alternate input methods support to St.Entry??

29 changes: 29 additions & 0 deletions todo@NotSirius-A/files/todo@NotSirius-A/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,33 @@

## [1.1] - 13.10.2025

### Added

- New toolbar function - Edit task name, which supports alternate input methods like ibus/fcitx
- Mark as important/not important functionality + toolbar button
- Added new border styling options
- New settings to accommodate important/not important functionality
- `edit_dialog_gtk.py` - Python script opening a Gtk dialog for editing task names
- `edit_dialog_gtk.ui` - Gtk dialog XML config
- Invisible padding without desklet decorations to make it easier to grab
- Options to disable/enable tooltips
- Added some visual indication when hovering over mark done/not done icons
- New options to disable/enable delete confirmation dialogs

### Changed

- Font size and icon sizes are now scaled by the system text scaling factor
- Changed default settings, changed some setting names/descriptions
- Now task names support multi line names + added text wrap
- `todo@NotSirius-A.pot` - updated translation table
- Minor changes to setting names/tooltips
- Update `README.MD`
- Minor improvements to `stylesheet.css`

### Fixed

- Incorrect width scaling width different display scales


## [1.0] - 26.06.2025

Expand Down
103 changes: 96 additions & 7 deletions todo@NotSirius-A/files/todo@NotSirius-A/MyToolBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const St = imports.gi.St;
const Lang = imports.lang;
const ModalDialog = imports.ui.modalDialog;
const Tooltips = imports.ui.tooltips;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;


/**
* Encapsulates functionality regarding the toolbar
Expand All @@ -27,7 +30,8 @@ class MyToolBar {
const th = this.desklet.theme["toolbar"];

this.container.style = "background-color:" + th["background_color"] + ";"
+ "color:" + th["font_color"] + ";";
+ "color:" + th["font_color"] + ";"
+ "border: solid " + th["border_width"] + "px " + th["border_color"] + ";";


let checked_icon_btn = new St.Button({style_class:"toolbar-icon-btn"});
Expand All @@ -45,11 +49,26 @@ class MyToolBar {
let remove_icon_btn = new St.Button({style_class:"toolbar-icon-btn"});
let remove_icon = new St.Icon({ icon_name: "list-remove-symbolic", icon_type: St.IconType.APPLICATION, icon_size: th["icon_size"], style_class:"toolbar-icon" });
remove_icon_btn.set_child(remove_icon);

let mark_important_icon_btn = new St.Button({style_class:"toolbar-icon-btn"});
let mark_important_icon = new St.Icon({ icon_name: "xapp-favorite-symbolic", icon_type: St.IconType.APPLICATION, icon_size: th["icon_size"], style_class:"toolbar-icon" });
mark_important_icon_btn.set_child(mark_important_icon);

let mark_unimportant_icon_btn = new St.Button({style_class:"toolbar-icon-btn"});
let mark_unimportant_icon = new St.Icon({ icon_name: "xapp-unfavorite-symbolic", icon_type: St.IconType.APPLICATION, icon_size: th["icon_size"], style_class:"toolbar-icon" });
mark_unimportant_icon_btn.set_child(mark_unimportant_icon);

let edit_icon_btn = new St.Button({style_class:"toolbar-icon-btn"});
let edit_icon = new St.Icon({ icon_name: "list-edit-symbolic", icon_type: St.IconType.APPLICATION, icon_size: th["icon_size"], style_class:"toolbar-icon" });
edit_icon_btn.set_child(edit_icon);

this.grid.attach(checked_icon_btn, 0, 0, 1, 1);
this.grid.attach(unchecked_icon_btn, 1, 0, 1, 1);
this.grid.attach(add_icon_btn, 2, 0, 1, 1);
this.grid.attach(remove_icon_btn, 3, 0, 1, 1);
this.grid.attach(mark_important_icon_btn, 4, 0, 1, 1);
this.grid.attach(mark_unimportant_icon_btn, 5, 0, 1, 1);
this.grid.attach(edit_icon_btn, 6, 0, 1, 1);


checked_icon_btn.connect("clicked", Lang.bind(this, function(a, event) {
Expand Down Expand Up @@ -79,26 +98,96 @@ class MyToolBar {
remove_icon_btn.connect("clicked", Lang.bind(this,() => {
const selected_num = this.desklet.TODOlist.getSelectedCount();

if (selected_num > 0) {

if (selected_num > 0 && this.desklet.areDeleteToolbarDialogsEnabled) {
let dialog = new ModalDialog.ConfirmDialog(
_(`Are you sure you want to remove ${selected_num} tasks?`),
() => {
this.desklet.TODOlist.removeItems(true);
this.desklet.TODOlist.saveItemsToSettings();
this.desklet.TODOlist.render();
}
);
dialog.open();
} else if (selected_num > 0){
this.desklet.TODOlist.removeItems(true);
this.desklet.TODOlist.render();
}
}));

new Tooltips.Tooltip(checked_icon_btn, "Mark selected tasks done");
new Tooltips.Tooltip(unchecked_icon_btn, "Mark selected tasks not done");
new Tooltips.Tooltip(add_icon_btn, "Add new task");
new Tooltips.Tooltip(remove_icon_btn, "Remove selected tasks");

mark_important_icon_btn.connect("clicked", Lang.bind(this, function(a, event) {

this.desklet.TODOlist.markItemsImportant(true, true);
this.desklet.TODOlist.deselectAllItems();

this.desklet.TODOlist.render();

}));

mark_unimportant_icon_btn.connect("clicked", Lang.bind(this, function(a, event) {

this.desklet.TODOlist.markItemsImportant(false, true);
this.desklet.TODOlist.deselectAllItems();

this.desklet.TODOlist.render();

}));

edit_icon_btn.connect("clicked", Lang.bind(this,() => {

const items = this.desklet.TODOlist.getSelected();

items.forEach(item => {
this.handleEditDialog(item);
});
this.desklet.TODOlist.deselectAllItems();

}));


if (this.desklet.areToolbarTooltipsEnabled) {
new Tooltips.Tooltip(checked_icon_btn, "Mark selected tasks as done");
new Tooltips.Tooltip(unchecked_icon_btn, "Mark selected tasks as not done");
new Tooltips.Tooltip(add_icon_btn, "Add new task");
new Tooltips.Tooltip(remove_icon_btn, "Remove selected tasks");
new Tooltips.Tooltip(mark_important_icon_btn, "Mark selected tasks as important");
new Tooltips.Tooltip(mark_unimportant_icon_btn, "Mark selected tasks as not important");
new Tooltips.Tooltip(edit_icon_btn, "Edit selected tasks");
}
}


handleEditDialog(item) {
try {

// Run an external Python script, to open a Gtk dialog which gives a more compatible input window
// This is to allow different input methods like ibus/fcitx
// I'm not sure if it's even possible to do this inside od JS, external Python solution is what I saw in a builtin cinnamon desklet launcher@cinnamon.org
const [success_, command_argv] = GLib.shell_parse_argv(`python3 ${this.desklet.DESKLET_ROOT}/edit_dialog_gtk.py "${item.name}"`);

const proc = Gio.Subprocess.new(
command_argv,
Gio.SubprocessFlags.STDOUT_PIPE
);

// Python script prints the output of the dialog when user accepts it, code here is retrieving this output from the stdout pipe
proc.wait_async(null, () => {
proc.communicate_utf8_async(null, null, (proc, result) => {

const [is_ok, name_out, _] = proc.communicate_utf8_finish(result);
if (is_ok) {
// imports.ui.main.notifyError(name_out);
item.name = name_out;
this.desklet.TODOlist.saveItemsToSettings();

this.desklet.TODOlist.render();
}
});
});

} catch (e) {
global.logError(e);
}
}

}
6 changes: 4 additions & 2 deletions todo@NotSirius-A/files/todo@NotSirius-A/TODOItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ const St = imports.gi.St;
* Represent a task item in a TODO list
*/
class TODOItem {
constructor(name, is_marked_done=false, is_selection_enabled = true) {
constructor(name, is_marked_done=false, is_marked_important=false, is_selection_enabled=true) {
this.is_selected = false;
this.name = name;
this.is_marked_done = is_marked_done;
this.actor = null;
this.StEntry = null;
this.is_selection_enabled = is_selection_enabled;
this.is_marked_important = is_marked_important;
}


getSettingsAttrs () {
return {
"name": this.name,
"is-marked-done": this.is_marked_done
"is-marked-done": this.is_marked_done,
"is-marked-important": this.is_marked_important
};
}

Expand Down
Loading