diff --git a/data/io.github.diegopvlk.Dosage.data.gresource.xml b/data/io.github.diegopvlk.Dosage.data.gresource.xml
index 06a4fc4..3107187 100644
--- a/data/io.github.diegopvlk.Dosage.data.gresource.xml
+++ b/data/io.github.diegopvlk.Dosage.data.gresource.xml
@@ -7,9 +7,10 @@
styles/style-dark.css
styles/card-colors.css
sounds/ding.ogg
- ui/window.ui
ui/med-dialog.ui
ui/preferences.ui
+ ui/refill-dialog.ui
+ ui/window.ui
icons/ui/today-icn-symbolic.svg
diff --git a/data/meson.build b/data/meson.build
index cda8489..7a80797 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -7,6 +7,7 @@ blueprints = custom_target(
'gtk/help-overlay.blp',
'ui/med-dialog.blp',
'ui/preferences.blp',
+ 'ui/refill-dialog.blp',
'ui/window.blp',
),
output: '.',
@@ -73,4 +74,4 @@ if compile_schemas.found()
)
endif
-subdir('icons')
\ No newline at end of file
+subdir('icons')
diff --git a/data/styles/style.css b/data/styles/style.css
index 872f09d..fec40ff 100644
--- a/data/styles/style.css
+++ b/data/styles/style.css
@@ -48,6 +48,10 @@ statuspage .title {
text-transform: capitalize;
}
+.refill-amount-btn {
+ padding: 7px 10px;
+}
+
dialog.about .boxed-list {
box-shadow: var(--list-border), var(--list-shadow);
border-radius: var(--list-border-radius);
@@ -350,10 +354,9 @@ button.floating:not(.suggested-action):active {
calc(alpha * 1)
);
}
-
-#medication-dialog
- button:not(.circular):not(.image-button):not(.arrow-button):not(.toggle):not(.am-pm) {
- min-width: 60px;
+#refill-dialog headerbar button,
+#medication-dialog headerbar button {
+ min-width: 50px;
}
#medication-dialog .linked-circular {
background: rgb(from currentColor r g b / calc(alpha * 0.1));
diff --git a/data/ui/refill-dialog.blp b/data/ui/refill-dialog.blp
new file mode 100644
index 0000000..7029432
--- /dev/null
+++ b/data/ui/refill-dialog.blp
@@ -0,0 +1,131 @@
+using Gtk 4.0;
+using Adw 1;
+
+Adw.Dialog refillDialog {
+ name: 'refill-dialog';
+ content-width: 424;
+ title: _("Refill");
+
+ Adw.ToolbarView {
+ [top]
+ Adw.HeaderBar headerBar {
+ show-start-title-buttons: false;
+ show-end-title-buttons: false;
+ decoration-layout: '';
+
+ [start]
+ Button cancelButton {
+ label: _("Cancel");
+ name: 'cancel';
+ }
+
+ [end]
+ Button saveButton {
+ styles [
+ 'suggested-action'
+ ]
+
+ label: _("Save");
+ name: 'save';
+ }
+ }
+
+ content: ScrolledWindow {
+ Adw.Clamp refillDialogClamp {
+ maximum-size: 400;
+
+ Box {
+ orientation: vertical;
+ margin-start: 10;
+ margin-end: 10;
+ margin-top: 15;
+ margin-bottom: 20;
+
+ ListBox {
+ styles [
+ "boxed-list-separate"
+ ]
+
+ selection-mode: none;
+
+ Adw.SpinRow refillRow {
+ styles [
+ "property"
+ ]
+
+ title: _("Inventory");
+ climb-rate: 0.2;
+ digits: 2;
+
+ adjustment: Adjustment refillInventory {
+ lower: 0;
+ upper: 99999;
+ step-increment: 1;
+ };
+ }
+ }
+
+ Box {
+ margin-top: 18;
+ margin-start: 2;
+ margin-end: 2;
+ spacing: 8;
+ halign: fill;
+ orientation: horizontal;
+ homogeneous: true;
+
+ Button refill5Button {
+ styles [
+ "refill-amount-btn",
+ "pill"
+ ]
+
+ label: "+5";
+ valign: center;
+ }
+
+ Button refill10Button {
+ styles [
+ "refill-amount-btn",
+ "pill"
+ ]
+
+ label: "+10";
+ valign: center;
+ }
+
+ Button refill30Button {
+ styles [
+ "refill-amount-btn",
+ "pill"
+ ]
+
+ label: "+30";
+ valign: center;
+ }
+
+ Button refill60Button {
+ styles [
+ "refill-amount-btn",
+ "pill"
+ ]
+
+ label: "+60";
+ valign: center;
+ }
+
+ Button refill100Button {
+ styles [
+ "refill-amount-btn",
+ "pill"
+ ]
+
+ label: "+100";
+ valign: center;
+ }
+ }
+ }
+ }
+ };
+ }
+}
diff --git a/src/io.github.diegopvlk.Dosage.src.gresource.xml b/src/io.github.diegopvlk.Dosage.src.gresource.xml
index ec5aeac..bf00002 100644
--- a/src/io.github.diegopvlk.Dosage.src.gresource.xml
+++ b/src/io.github.diegopvlk.Dosage.src.gresource.xml
@@ -6,6 +6,7 @@
window.js
medDialog.js
prefsDialog.js
+ refillDialog.js
medication.js
todayFactory.js
historyFactory.js
diff --git a/src/medDialog.js b/src/medDialog.js
index 62aa96d..4e85990 100644
--- a/src/medDialog.js
+++ b/src/medDialog.js
@@ -551,9 +551,12 @@ export function openMedicationDialog(DosageWindow, list, position, mode) {
i.obj.inventory.current += diff - adjusts;
}
if (tempInv === i.obj.inventory.current) break;
- DosageWindow._treatmentsList.model = new Gtk.NoSelection({
- model: treatmentsLS,
- });
+
+ // reload-ish of treatments list
+ // necessary for updating low stock and cycle date labels
+ DosageWindow._treatmentsList.visible = false;
+ DosageWindow._treatmentsList.visible = true;
+
DosageWindow._updateJsonFile('treatments', treatmentsLS);
DosageWindow._checkInventory();
}
diff --git a/src/refillDialog.js b/src/refillDialog.js
new file mode 100644
index 0000000..8bd7b2e
--- /dev/null
+++ b/src/refillDialog.js
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 Diego Povliuk
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+'use strict';
+
+import Gdk from 'gi://Gdk';
+import Gtk from 'gi://Gtk';
+
+import { DosageApplication } from './main.js';
+
+export function openRefillDialog(listItem, position) {
+ const item = listItem.get_item().obj;
+ const DosageWindow = DosageApplication.get_default().activeWindow;
+ const builder = Gtk.Builder.new_from_resource('/io/github/diegopvlk/Dosage/ui/refill-dialog.ui');
+
+ const refillDialog = builder.get_object('refillDialog');
+ const refillRow = builder.get_object('refillRow');
+ const refillInv = builder.get_object('refillInventory');
+ const refill5Btn = builder.get_object('refill5Button');
+ const refill10Btn = builder.get_object('refill10Button');
+ const refill30Btn = builder.get_object('refill30Button');
+ const refill60Btn = builder.get_object('refill60Button');
+ const refill100Btn = builder.get_object('refill100Button');
+ const cancelButton = builder.get_object('cancelButton');
+ const saveButton = builder.get_object('saveButton');
+
+ const keyController = new Gtk.EventControllerKey();
+ keyController.connect('key-pressed', (_, keyval, keycode, state) => {
+ const shiftPressed = (state & Gdk.ModifierType.SHIFT_MASK) !== 0;
+ const controlPressed = (state & Gdk.ModifierType.CONTROL_MASK) !== 0;
+ const enterPressed = keyval === Gdk.KEY_Return;
+
+ if ((controlPressed || shiftPressed) && enterPressed) {
+ saveButton.activate();
+ }
+ });
+ refillDialog.add_controller(keyController);
+
+ refillRow.subtitle = item.name;
+
+ refillInv.value = item.inventory.current;
+
+ refill5Btn.connect('clicked', () => (refillInv.value += 5));
+ refill10Btn.connect('clicked', () => (refillInv.value += 10));
+ refill30Btn.connect('clicked', () => (refillInv.value += 30));
+ refill60Btn.connect('clicked', () => (refillInv.value += 60));
+ refill100Btn.connect('clicked', () => (refillInv.value += 100));
+
+ cancelButton.connect('clicked', () => refillDialog.force_close());
+
+ saveButton.connect('clicked', () => {
+ item.inventory.current = refillInv.value;
+
+ DosageWindow._updateEverything(['skipHistUp', position]);
+ DosageWindow._scheduleNotifications('saving');
+
+ refillDialog.force_close();
+ });
+
+ const refillDialogClamp = builder.get_object('refillDialogClamp');
+ const [refillDialogClampHeight] = refillDialogClamp.measure(Gtk.Orientation.VERTICAL, -1);
+ refillDialog.content_height = refillDialogClampHeight + 48;
+
+ refillDialog.present(DosageWindow);
+}
diff --git a/src/treatmentsFactory.js b/src/treatmentsFactory.js
index 03bff3b..22de0e3 100644
--- a/src/treatmentsFactory.js
+++ b/src/treatmentsFactory.js
@@ -11,6 +11,7 @@ import Pango from 'gi://Pango';
import { getSpecificDaysLabel } from './utils.js';
import { DosageApplication } from './main.js';
import { confirmDeleteDialog } from './medDialog.js';
+import { openRefillDialog } from './refillDialog.js';
export const treatmentsFactory = new Gtk.SignalListItemFactory();
@@ -88,10 +89,17 @@ treatmentsFactory.connect('bind', (factory, listItem) => {
const today = new Date().setHours(0, 0, 0, 0);
const start = new Date(item.duration.start).setHours(0, 0, 0, 0);
const end = new Date(item.duration.end).setHours(0, 0, 0, 0);
+ const inv = item.inventory;
const DosageWindow = DosageApplication.get_default().activeWindow;
const app = DosageWindow.get_application();
+ if (inv.enabled) {
+ const refillAct = new Gio.SimpleAction({ name: `refillMed${pos}` });
+ refillAct.connect('activate', () => openRefillDialog(listItem, pos));
+ app.add_action(refillAct);
+ }
+
const duplicateAct = new Gio.SimpleAction({ name: `duplicateMed${pos}` });
duplicateAct.connect('activate', () => {
const list = DosageWindow._treatmentsList;
@@ -103,10 +111,11 @@ treatmentsFactory.connect('bind', (factory, listItem) => {
deleteAct.connect('activate', () => confirmDeleteDialog(item, pos, DosageWindow));
app.add_action(deleteAct);
+ const refillMed = Gio.MenuItem.new(_('Refill'), `app.refillMed${pos}`);
const duplicateMed = Gio.MenuItem.new(_('Duplicate'), `app.duplicateMed${pos}`);
const deleteMed = Gio.MenuItem.new(_('Delete'), `app.deleteMed${pos}`);
- optionsMenu.remove_all();
+ optionsMenu.append_item(refillMed);
optionsMenu.append_item(duplicateMed);
optionsMenu.append_item(deleteMed);
@@ -122,8 +131,6 @@ treatmentsFactory.connect('bind', (factory, listItem) => {
nameLabel.label = item.name;
- const inv = item.inventory;
-
if (inv.enabled) {
let currInv = inv.current < 0 ? 0 : inv.current;
@@ -202,20 +209,34 @@ treatmentsFactory.connect('bind', (factory, listItem) => {
box.add_css_class(item.color);
icon.icon_name = item.icon;
+});
- function formatDate(date, weekday) {
- if (weekday) {
- return new Date(date).toLocaleDateString(undefined, {
- weekday: 'short',
- month: 'short',
- day: 'numeric',
- });
- }
+treatmentsFactory.connect('unbind', (factory, listItem) => {
+ const DosageWindow = DosageApplication.get_default().activeWindow;
+ const app = DosageWindow.get_application();
+ const box = listItem.get_child();
+ const pos = listItem.get_position();
+ const optionsMenu = box.get_last_child().get_menu_model();
+
+ app.remove_action(`refillMed${pos}`);
+ app.remove_action(`duplicateMed${pos}`);
+ app.remove_action(`deleteMed${pos}`);
+ optionsMenu.remove_all();
+});
+
+function formatDate(date, weekday) {
+ if (weekday) {
return new Date(date).toLocaleDateString(undefined, {
+ weekday: 'short',
month: 'short',
day: 'numeric',
- year: 'numeric',
});
}
-});
+
+ return new Date(date).toLocaleDateString(undefined, {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric',
+ });
+}