Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b11176015 | |||
| 154837d411 | |||
| c1acd2dfcb |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
subprojects/blueprint-compiler
|
subprojects/blueprint-compiler
|
||||||
|
_build
|
||||||
18
README.md
18
README.md
@@ -5,10 +5,10 @@ This is a companion-app for Warframe players on Linux, written in phython and GT
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Manage a persistent list of which Basic and Prime warframes you own to help you plan what to get next!
|
- Manage a persistent list of which Basic and Prime warframes you own to help you plan what to get next!
|
||||||
|
- Wishlist missing frames and track which parts you own and where to get the missing ones!
|
||||||
|
|
||||||
## Planned
|
## Planned
|
||||||
|
|
||||||
- Overview of needed parts, with ability to select which are already acquired, what is needed to craft them, and where/how to optain them.
|
|
||||||
- Overview of which frames have been subsumed.
|
- Overview of which frames have been subsumed.
|
||||||
- Overview of which relics to prioritize based on which frames are missing or a wishlist of priority frames. (Potentially also integrate market data?)
|
- Overview of which relics to prioritize based on which frames are missing or a wishlist of priority frames. (Potentially also integrate market data?)
|
||||||
- Support for weapons.
|
- Support for weapons.
|
||||||
@@ -21,7 +21,21 @@ Ideally, cloning the repo and opening with GNOME Builder will have the best resu
|
|||||||
$ git clone https://git.valhrafnaz.gay/valhrafnaz/VoidManifest.git
|
$ git clone https://git.valhrafnaz.gay/valhrafnaz/VoidManifest.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you can run ninja yourself.
|
Alternatively, you can build the project yourself.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd VoidManifest
|
||||||
|
$ meson setup builddir
|
||||||
|
$ meson compile -C builddir
|
||||||
|
```
|
||||||
|
|
||||||
|
To be able to run the executable `./builddir/src/voidmanifest`, you must install the project with meson:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ meson install -C builddir
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that only the flatpak version of the program is supported, all issues referencing local installations will be deleted.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ template $ChecklistPage: Box {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
label: _("List of all Warframes available as of Dec 10 '25");
|
label: _("List of all Warframes available as of today");
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
|
|
||||||
styles [
|
styles [
|
||||||
"dim-label",
|
"dimmed",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ template $ChecklistPage: Box {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton cyte09 {
|
ToggleButton cyte09 {
|
||||||
name: "cyte09";
|
name: "cyte-09";
|
||||||
label: _("Cyte-09");
|
label: _("Cyte-09");
|
||||||
margin-bottom: 4;
|
margin-bottom: 4;
|
||||||
}
|
}
|
||||||
@@ -622,7 +622,7 @@ template $ChecklistPage: Box {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton cyte09_prime {
|
ToggleButton cyte09_prime {
|
||||||
name: "cyte09_prime";
|
name: "cyte-09_prime";
|
||||||
label: _("Cyte-09 Prime");
|
label: _("Cyte-09 Prime");
|
||||||
margin-bottom: 4;
|
margin-bottom: 4;
|
||||||
can-target: false;
|
can-target: false;
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel">
|
<object class="GtkLabel">
|
||||||
<property name="label" translatable="yes">List of all Warframes available as of Dec 10 '25</property>
|
<property name="label" translatable="yes">List of all Warframes available as of today</property>
|
||||||
<property name="margin-bottom">12</property>
|
<property name="margin-bottom">12</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="dim-label"/>
|
<class name="dimmed"/>
|
||||||
</style>
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
@@ -189,7 +189,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkToggleButton" id="cyte09">
|
<object class="GtkToggleButton" id="cyte09">
|
||||||
<property name="name">cyte09</property>
|
<property name="name">cyte-09</property>
|
||||||
<property name="label" translatable="yes">Cyte-09</property>
|
<property name="label" translatable="yes">Cyte-09</property>
|
||||||
<property name="margin-bottom">4</property>
|
<property name="margin-bottom">4</property>
|
||||||
</object>
|
</object>
|
||||||
@@ -740,7 +740,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkToggleButton" id="cyte09_prime">
|
<object class="GtkToggleButton" id="cyte09_prime">
|
||||||
<property name="name">cyte09_prime</property>
|
<property name="name">cyte-09_prime</property>
|
||||||
<property name="label" translatable="yes">Cyte-09 Prime</property>
|
<property name="label" translatable="yes">Cyte-09 Prime</property>
|
||||||
<property name="margin-bottom">4</property>
|
<property name="margin-bottom">4</property>
|
||||||
<property name="can-target">false</property>
|
<property name="can-target">false</property>
|
||||||
|
|||||||
@@ -5,12 +5,17 @@ template $HomePage: Box {
|
|||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
baseline-position: center;
|
baseline-position: center;
|
||||||
Adw.Clamp {
|
Adw.Clamp {
|
||||||
Adw.PreferencesGroup {
|
ScrolledWindow {
|
||||||
|
hexpand: true;
|
||||||
|
vexpand: true;
|
||||||
|
child: Adw.PreferencesGroup {
|
||||||
|
margin-start:20;
|
||||||
|
margin-end: 20;
|
||||||
title: _("Welcome back, Tenno");
|
title: _("Welcome back, Tenno");
|
||||||
styles [
|
styles [
|
||||||
'list-title'
|
'list-title'
|
||||||
]
|
]
|
||||||
Adw.ActionRow {
|
Adw.ExpanderRow owned_frames_row {
|
||||||
title: _("Owned Unique Frames:");
|
title: _("Owned Unique Frames:");
|
||||||
title-selectable: false;
|
title-selectable: false;
|
||||||
activatable: false;
|
activatable: false;
|
||||||
@@ -19,7 +24,7 @@ template $HomePage: Box {
|
|||||||
Label owned_frames {
|
Label owned_frames {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow {
|
Adw.ExpanderRow owned_basics_row {
|
||||||
title: _("Owned Basic Frames:");
|
title: _("Owned Basic Frames:");
|
||||||
title-selectable: false;
|
title-selectable: false;
|
||||||
activatable: false;
|
activatable: false;
|
||||||
@@ -29,7 +34,7 @@ template $HomePage: Box {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.ActionRow {
|
Adw.ExpanderRow missing_basics_row {
|
||||||
title: _("Missing Basic Frames:");
|
title: _("Missing Basic Frames:");
|
||||||
title-selectable: false;
|
title-selectable: false;
|
||||||
activatable: false;
|
activatable: false;
|
||||||
@@ -39,7 +44,7 @@ template $HomePage: Box {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.ActionRow {
|
Adw.ExpanderRow owned_primes_row {
|
||||||
title: _("Owned Prime Frames:");
|
title: _("Owned Prime Frames:");
|
||||||
title-selectable: false;
|
title-selectable: false;
|
||||||
activatable: false;
|
activatable: false;
|
||||||
@@ -49,7 +54,7 @@ template $HomePage: Box {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.ActionRow {
|
Adw.ExpanderRow missing_primes_row {
|
||||||
title: _("Missing Prime Frames:");
|
title: _("Missing Prime Frames:");
|
||||||
title-selectable: false;
|
title-selectable: false;
|
||||||
activatable: false;
|
activatable: false;
|
||||||
@@ -58,6 +63,7 @@ template $HomePage: Box {
|
|||||||
Label missing_primes {
|
Label missing_primes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,13 +12,19 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
<child>
|
<child>
|
||||||
<object class="AdwClamp">
|
<object class="AdwClamp">
|
||||||
<child>
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<property name="vexpand">true</property>
|
||||||
|
<property name="child">
|
||||||
<object class="AdwPreferencesGroup">
|
<object class="AdwPreferencesGroup">
|
||||||
|
<property name="margin-start">20</property>
|
||||||
|
<property name="margin-end">20</property>
|
||||||
<property name="title" translatable="yes">Welcome back, Tenno</property>
|
<property name="title" translatable="yes">Welcome back, Tenno</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="list-title"/>
|
<class name="list-title"/>
|
||||||
</style>
|
</style>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow">
|
<object class="AdwExpanderRow" id="owned_frames_row">
|
||||||
<property name="title" translatable="yes">Owned Unique Frames:</property>
|
<property name="title" translatable="yes">Owned Unique Frames:</property>
|
||||||
<property name="title-selectable">false</property>
|
<property name="title-selectable">false</property>
|
||||||
<property name="activatable">false</property>
|
<property name="activatable">false</property>
|
||||||
@@ -28,7 +34,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow">
|
<object class="AdwExpanderRow" id="owned_basics_row">
|
||||||
<property name="title" translatable="yes">Owned Basic Frames:</property>
|
<property name="title" translatable="yes">Owned Basic Frames:</property>
|
||||||
<property name="title-selectable">false</property>
|
<property name="title-selectable">false</property>
|
||||||
<property name="activatable">false</property>
|
<property name="activatable">false</property>
|
||||||
@@ -38,7 +44,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow">
|
<object class="AdwExpanderRow" id="missing_basics_row">
|
||||||
<property name="title" translatable="yes">Missing Basic Frames:</property>
|
<property name="title" translatable="yes">Missing Basic Frames:</property>
|
||||||
<property name="title-selectable">false</property>
|
<property name="title-selectable">false</property>
|
||||||
<property name="activatable">false</property>
|
<property name="activatable">false</property>
|
||||||
@@ -48,7 +54,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow">
|
<object class="AdwExpanderRow" id="owned_primes_row">
|
||||||
<property name="title" translatable="yes">Owned Prime Frames:</property>
|
<property name="title" translatable="yes">Owned Prime Frames:</property>
|
||||||
<property name="title-selectable">false</property>
|
<property name="title-selectable">false</property>
|
||||||
<property name="activatable">false</property>
|
<property name="activatable">false</property>
|
||||||
@@ -58,7 +64,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow">
|
<object class="AdwExpanderRow" id="missing_primes_row">
|
||||||
<property name="title" translatable="yes">Missing Prime Frames:</property>
|
<property name="title" translatable="yes">Missing Prime Frames:</property>
|
||||||
<property name="title-selectable">false</property>
|
<property name="title-selectable">false</property>
|
||||||
<property name="activatable">false</property>
|
<property name="activatable">false</property>
|
||||||
@@ -68,6 +74,8 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ template $VoidmanifestWindow: Adw.ApplicationWindow {
|
|||||||
// Main View
|
// Main View
|
||||||
content:
|
content:
|
||||||
Adw.ViewStack viewstack {
|
Adw.ViewStack viewstack {
|
||||||
|
notify::visible-child => $on_page_changed();
|
||||||
};
|
};
|
||||||
[bottom]
|
[bottom]
|
||||||
ActionBar {
|
ActionBar {
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<property name="content">
|
<property name="content">
|
||||||
<object class="AdwViewStack" id="viewstack"></object>
|
<object class="AdwViewStack" id="viewstack">
|
||||||
|
<signal name="notify::visible-child" handler="on_page_changed"/>
|
||||||
|
</object>
|
||||||
</property>
|
</property>
|
||||||
<child type="bottom">
|
<child type="bottom">
|
||||||
<object class="GtkActionBar">
|
<object class="GtkActionBar">
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ template $WishlistPage: Gtk.Box {
|
|||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
baseline-position: center;
|
baseline-position: center;
|
||||||
Adw.Clamp {
|
Adw.Clamp {
|
||||||
|
ScrolledWindow {
|
||||||
|
hexpand: true;
|
||||||
|
vexpand: true;
|
||||||
Adw.PreferencesGroup wishlist {
|
Adw.PreferencesGroup wishlist {
|
||||||
|
margin-end:20;
|
||||||
header-suffix: Button btn_wishlist_add {
|
header-suffix: Button btn_wishlist_add {
|
||||||
icon-name: 'plus-circle-outline-symbolic';
|
icon-name: 'plus-circle-outline-symbolic';
|
||||||
styles [
|
styles [
|
||||||
@@ -21,4 +25,5 @@ template $WishlistPage: Gtk.Box {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,13 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
<property name="baseline-position">1</property>
|
<property name="baseline-position">1</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwClamp">
|
<object class="AdwClamp">
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="hexpand">true</property>
|
||||||
|
<property name="vexpand">true</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwPreferencesGroup" id="wishlist">
|
<object class="AdwPreferencesGroup" id="wishlist">
|
||||||
|
<property name="margin-end">20</property>
|
||||||
<property name="header-suffix">
|
<property name="header-suffix">
|
||||||
<object class="GtkButton" id="btn_wishlist_add">
|
<object class="GtkButton" id="btn_wishlist_add">
|
||||||
<property name="icon-name">plus-circle-outline-symbolic</property>
|
<property name="icon-name">plus-circle-outline-symbolic</property>
|
||||||
@@ -32,5 +37,7 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
|
|||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</template>
|
</template>
|
||||||
</interface>
|
</interface>
|
||||||
@@ -6,7 +6,7 @@ EXISTING_FRAMES_BASIC = [
|
|||||||
"caliban",
|
"caliban",
|
||||||
"chroma",
|
"chroma",
|
||||||
"citrine",
|
"citrine",
|
||||||
"cyte09",
|
"cyte-09",
|
||||||
"dagath",
|
"dagath",
|
||||||
"dante",
|
"dante",
|
||||||
"ember",
|
"ember",
|
||||||
@@ -52,7 +52,6 @@ EXISTING_FRAMES_BASIC = [
|
|||||||
"temple",
|
"temple",
|
||||||
"titania",
|
"titania",
|
||||||
"trinity",
|
"trinity",
|
||||||
"uriel",
|
|
||||||
"valkyr",
|
"valkyr",
|
||||||
"vauban",
|
"vauban",
|
||||||
"volt",
|
"volt",
|
||||||
@@ -79,7 +78,6 @@ EXISTING_FRAMES_PRIME = [
|
|||||||
"garuda_prime",
|
"garuda_prime",
|
||||||
"gauss_prime",
|
"gauss_prime",
|
||||||
"grendel_prime",
|
"grendel_prime",
|
||||||
"gyre_prime",
|
|
||||||
"harrow_prime",
|
"harrow_prime",
|
||||||
"hildryn_prime",
|
"hildryn_prime",
|
||||||
"hydroid_prime",
|
"hydroid_prime",
|
||||||
@@ -119,7 +117,6 @@ EXISTING_FRAMES_PRIME = [
|
|||||||
ID_TO_NAME: dict[str, str] = dict()
|
ID_TO_NAME: dict[str, str] = dict()
|
||||||
NAME_TO_ID: dict[str, str] = dict()
|
NAME_TO_ID: dict[str, str] = dict()
|
||||||
# warframe counts
|
# warframe counts
|
||||||
# TODO Update via API/wiki
|
|
||||||
BASIC_WARFRAMES = 63
|
BASIC_WARFRAMES = 63
|
||||||
PRIME_WARFRAMES = 49
|
PRIME_WARFRAMES = 49
|
||||||
CURRENT_UPDATE = 41
|
CURRENT_UPDATE = 41
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ class VoidmanifestApplication(Adw.Application):
|
|||||||
|
|
||||||
def _refresh_metadata(self):
|
def _refresh_metadata(self):
|
||||||
url = 'https://api.warframestat.us/warframes'
|
url = 'https://api.warframestat.us/warframes'
|
||||||
params = 'only=name,isPrime'
|
params = 'only=name,isPrime,category'
|
||||||
resp = requests.get(url, params)
|
resp = requests.get(url, params)
|
||||||
base_frames_list: list[str] = list()
|
base_frames_list: list[str] = list()
|
||||||
prime_frames_list: list[str] = list()
|
prime_frames_list: list[str] = list()
|
||||||
@@ -135,6 +135,9 @@ class VoidmanifestApplication(Adw.Application):
|
|||||||
else:
|
else:
|
||||||
print('CRITICAL ERROR: Could not refresh metadata from https://api.warframestat.us/')
|
print('CRITICAL ERROR: Could not refresh metadata from https://api.warframestat.us/')
|
||||||
for warframe in resp_data:
|
for warframe in resp_data:
|
||||||
|
# skip every non-warframe
|
||||||
|
if warframe['category'] != 'Warframes':
|
||||||
|
continue
|
||||||
name = warframe['name']
|
name = warframe['name']
|
||||||
# skip these two frames since they cannot be acquired anymore and break symmetry in the list
|
# skip these two frames since they cannot be acquired anymore and break symmetry in the list
|
||||||
if name == 'Excalibur Umbra Prime' or name == 'Excalibur Prime':
|
if name == 'Excalibur Umbra Prime' or name == 'Excalibur Prime':
|
||||||
@@ -145,12 +148,23 @@ class VoidmanifestApplication(Adw.Application):
|
|||||||
prime_frames_list.append(frame_id)
|
prime_frames_list.append(frame_id)
|
||||||
else:
|
else:
|
||||||
base_frames_list.append(frame_id)
|
base_frames_list.append(frame_id)
|
||||||
|
# TODO REMOVE
|
||||||
|
# only needed until API is finally updated
|
||||||
|
base_frames_list.append('uriel')
|
||||||
|
prime_frames_list.append('gyre_prime')
|
||||||
|
base_frames_list = sorted(list(set(base_frames_list)))
|
||||||
|
prime_frames_list = sorted(list(set(prime_frames_list)))
|
||||||
EXISTING_FRAMES_BASIC = base_frames_list.copy()
|
EXISTING_FRAMES_BASIC = base_frames_list.copy()
|
||||||
EXISTING_FRAMES_PRIME = prime_frames_list.copy()
|
EXISTING_FRAMES_PRIME = prime_frames_list.copy()
|
||||||
BASIC_WARFRAMES = len(EXISTING_FRAMES_BASIC)
|
BASIC_WARFRAMES = len(EXISTING_FRAMES_BASIC)
|
||||||
PRIME_WARFRAMES = len(EXISTING_FRAMES_PRIME)
|
PRIME_WARFRAMES = len(EXISTING_FRAMES_PRIME)
|
||||||
# dictionary that translates ids to names for better responsiveness on UI
|
# dictionary that translates ids to names for better responsiveness on UI
|
||||||
ID_TO_NAME[frame_id] = name
|
ID_TO_NAME[frame_id] = name
|
||||||
|
# TODO REMOVE
|
||||||
|
# only needed until API is finally updated
|
||||||
|
ID_TO_NAME['gyre_prime'] = 'Gyre Prime'
|
||||||
|
ID_TO_NAME['uriel'] = 'Uriel'
|
||||||
|
|
||||||
NAME_TO_ID = dict((v,k) for k,v in ID_TO_NAME.items())
|
NAME_TO_ID = dict((v,k) for k,v in ID_TO_NAME.items())
|
||||||
|
|
||||||
|
|
||||||
@@ -170,7 +184,7 @@ class VoidmanifestApplication(Adw.Application):
|
|||||||
json.dump(profile.to_dict(), f, indent=2)
|
json.dump(profile.to_dict(), f, indent=2)
|
||||||
|
|
||||||
def reset_frames(self, param):
|
def reset_frames(self, param):
|
||||||
self.profile.owned_frames = {}
|
self.profile.owned_frames = []
|
||||||
button = self.checklist_page.btns_basic.get_first_child()
|
button = self.checklist_page.btns_basic.get_first_child()
|
||||||
while button is not None:
|
while button is not None:
|
||||||
if isinstance(button, Gtk.ToggleButton):
|
if isinstance(button, Gtk.ToggleButton):
|
||||||
@@ -188,7 +202,7 @@ class VoidmanifestApplication(Adw.Application):
|
|||||||
basic_count = 0
|
basic_count = 0
|
||||||
missing_basics: list[str] = []
|
missing_basics: list[str] = []
|
||||||
for basic_frame in EXISTING_FRAMES_BASIC:
|
for basic_frame in EXISTING_FRAMES_BASIC:
|
||||||
if self.profile.owned_frames.get(basic_frame, False):
|
if basic_frame in self.profile.owned_frames:
|
||||||
basic_count = basic_count + 1
|
basic_count = basic_count + 1
|
||||||
else:
|
else:
|
||||||
missing_basics.append(basic_frame)
|
missing_basics.append(basic_frame)
|
||||||
@@ -196,18 +210,18 @@ class VoidmanifestApplication(Adw.Application):
|
|||||||
prime_count = 0
|
prime_count = 0
|
||||||
missing_primes: list[str] = []
|
missing_primes: list[str] = []
|
||||||
for prime_frame in EXISTING_FRAMES_PRIME:
|
for prime_frame in EXISTING_FRAMES_PRIME:
|
||||||
if self.profile.owned_frames.get(prime_frame, False):
|
if prime_frame in self.profile.owned_frames:
|
||||||
prime_count = prime_count + 1
|
prime_count = prime_count + 1
|
||||||
else:
|
else:
|
||||||
missing_primes.append(prime_frame)
|
missing_primes.append(prime_frame)
|
||||||
# count unique
|
# count unique
|
||||||
unique_frames: set[str] = set()
|
unique_frames: set[str] = set()
|
||||||
for owned_frame, is_owned in self.profile.owned_frames.items():
|
for owned_frame in self.profile.owned_frames:
|
||||||
if is_owned:
|
|
||||||
_name = owned_frame.removesuffix("_prime")
|
_name = owned_frame.removesuffix("_prime")
|
||||||
_name = _name.removesuffix("_umbra")
|
_name = _name.removesuffix("_umbra")
|
||||||
unique_frames.add(_name)
|
unique_frames.add(_name)
|
||||||
|
|
||||||
|
self.profile.unique_frames_list = list(unique_frames)
|
||||||
self.profile.missing_basics_count = len(missing_basics)
|
self.profile.missing_basics_count = len(missing_basics)
|
||||||
self.profile.missing_primes_count = len(missing_primes)
|
self.profile.missing_primes_count = len(missing_primes)
|
||||||
self.profile.unique_frames_owned = len(unique_frames)
|
self.profile.unique_frames_owned = len(unique_frames)
|
||||||
|
|||||||
@@ -44,14 +44,14 @@ class ChecklistPage(Gtk.Box):
|
|||||||
while button is not None:
|
while button is not None:
|
||||||
if isinstance(button, Gtk.ToggleButton):
|
if isinstance(button, Gtk.ToggleButton):
|
||||||
btn_id = button.get_name()
|
btn_id = button.get_name()
|
||||||
button.set_active(self.app.profile.owned_frames.get(btn_id, False))
|
button.set_active((btn_id in self.app.profile.owned_frames))
|
||||||
button.connect("toggled", self._on_button_toggled)
|
button.connect("toggled", self._on_button_toggled)
|
||||||
button = button.get_next_sibling()
|
button = button.get_next_sibling()
|
||||||
button = self.btns_prime.get_first_child()
|
button = self.btns_prime.get_first_child()
|
||||||
while button is not None:
|
while button is not None:
|
||||||
if isinstance(button, Gtk.ToggleButton):
|
if isinstance(button, Gtk.ToggleButton):
|
||||||
btn_id = button.get_name()
|
btn_id = button.get_name()
|
||||||
button.set_active(self.app.profile.owned_frames.get(btn_id, False))
|
button.set_active((btn_id in self.app.profile.owned_frames))
|
||||||
button.connect("toggled", self._on_button_toggled)
|
button.connect("toggled", self._on_button_toggled)
|
||||||
button = button.get_next_sibling()
|
button = button.get_next_sibling()
|
||||||
|
|
||||||
@@ -90,7 +90,12 @@ class ChecklistPage(Gtk.Box):
|
|||||||
if btn_id == "GtkToggleButton":
|
if btn_id == "GtkToggleButton":
|
||||||
print("what")
|
print("what")
|
||||||
return
|
return
|
||||||
self.app.profile.owned_frames[btn_id] = btn.get_active()
|
if btn.get_active():
|
||||||
|
self.app.profile.owned_frames.append(btn_id)
|
||||||
|
if btn_id in self.app.profile.wishlist(btn_id):
|
||||||
|
self.app.profile.wishlist.remove(btn_id)
|
||||||
|
else:
|
||||||
|
self.app.profile.owned_frames.remove(btn_id)
|
||||||
self.app.save_profile(self.app.profile)
|
self.app.save_profile(self.app.profile)
|
||||||
self._calc_frames()
|
self._calc_frames()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import gi
|
import gi
|
||||||
|
|
||||||
from ..constants import BASIC_WARFRAMES, PRIME_WARFRAMES
|
from ..constants import BASIC_WARFRAMES, PRIME_WARFRAMES, ID_TO_NAME
|
||||||
|
|
||||||
gi.require_version('Gtk', '4.0')
|
gi.require_version('Gtk', '4.0')
|
||||||
gi.require_version('Adw', '1')
|
gi.require_version('Adw', '1')
|
||||||
@@ -12,29 +12,80 @@ from gi.repository import Gtk, Adw, GObject, GLib # noqa: E402
|
|||||||
class HomePage(Gtk.Box):
|
class HomePage(Gtk.Box):
|
||||||
__gtype_name__ = "HomePage"
|
__gtype_name__ = "HomePage"
|
||||||
|
|
||||||
|
app = None
|
||||||
|
|
||||||
owned_frames = Gtk.Template.Child()
|
owned_frames = Gtk.Template.Child()
|
||||||
|
owned_frames_row = Gtk.Template.Child()
|
||||||
owned_basics = Gtk.Template.Child()
|
owned_basics = Gtk.Template.Child()
|
||||||
|
owned_basics_row = Gtk.Template.Child()
|
||||||
missing_basics = Gtk.Template.Child()
|
missing_basics = Gtk.Template.Child()
|
||||||
|
missing_basics_row = Gtk.Template.Child()
|
||||||
owned_primes = Gtk.Template.Child()
|
owned_primes = Gtk.Template.Child()
|
||||||
|
owned_primes_row = Gtk.Template.Child()
|
||||||
missing_primes = Gtk.Template.Child()
|
missing_primes = Gtk.Template.Child()
|
||||||
|
missing_primes_row = Gtk.Template.Child()
|
||||||
|
|
||||||
def __init__(self, *, parent: Adw.ViewStack):
|
def __init__(self, *, parent: Adw.ViewStack):
|
||||||
"""
|
|
||||||
`parent` is the ViewStack that will host this page.
|
|
||||||
By passing it to the superclass constructor we tell GTK to
|
|
||||||
insert the newly created page into that stack automatically.
|
|
||||||
"""
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
parent.add_titled(self, "home", "Home")
|
parent.add_titled(self, "home", "Home")
|
||||||
wrapper = parent.get_page(self)
|
wrapper = parent.get_page(self)
|
||||||
wrapper.set_icon_name("compass2-symbolic")
|
wrapper.set_icon_name("compass2-symbolic")
|
||||||
self.app = self.get_root().get_application()
|
self.app = self.get_root().get_application()
|
||||||
self.app.profile._on_change = self.refresh
|
self._unique_rows: dict[str, Adw.ActionRow] = {}
|
||||||
|
self._missing_basics_rows: dict[str, Adw.ActionRow] = {}
|
||||||
|
self._missing_primes_rows: dict[str, Adw.ActionRow] = {}
|
||||||
|
self._basic_rows: dict[str, Adw.ActionRow] = {}
|
||||||
|
self._prime_rows: dict[str, Adw.ActionRow] = {}
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
|
# Prevents race condition where refresh is called before __init__
|
||||||
|
if self.app is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.app.profile.owned_frames = sorted(list(set(self.app.profile.owned_frames)))
|
||||||
|
|
||||||
|
# set labels
|
||||||
self.owned_frames.set_label(str (self.app.profile.unique_frames_owned))
|
self.owned_frames.set_label(str (self.app.profile.unique_frames_owned))
|
||||||
self.owned_basics.set_label(str (BASIC_WARFRAMES - self.app.profile.missing_basics_count))
|
self.owned_basics.set_label(str (BASIC_WARFRAMES - self.app.profile.missing_basics_count))
|
||||||
self.owned_primes.set_label(str (PRIME_WARFRAMES - self.app.profile.missing_primes_count))
|
self.owned_primes.set_label(str (PRIME_WARFRAMES - self.app.profile.missing_primes_count))
|
||||||
self.missing_basics.set_label(str(self.app.profile.missing_basics_count))
|
self.missing_basics.set_label(str(self.app.profile.missing_basics_count))
|
||||||
self.missing_primes.set_label(str(self.app.profile.missing_primes_count))
|
self.missing_primes.set_label(str(self.app.profile.missing_primes_count))
|
||||||
|
# populate dropdowns
|
||||||
|
for frame in self.app.profile.owned_frames:
|
||||||
|
if "_prime" in frame or "_umbra" in frame:
|
||||||
|
row = Adw.ActionRow(
|
||||||
|
title=ID_TO_NAME[frame]
|
||||||
|
)
|
||||||
|
if not self._prime_rows.get(frame, False):
|
||||||
|
self._prime_rows[frame] = row
|
||||||
|
self.owned_primes_row.add_row(row)
|
||||||
|
else:
|
||||||
|
row = Adw.ActionRow(
|
||||||
|
title=ID_TO_NAME[frame]
|
||||||
|
)
|
||||||
|
if not self._basic_rows.get(frame, False):
|
||||||
|
self._basic_rows[frame] = row
|
||||||
|
self.owned_basics_row.add_row(row)
|
||||||
|
|
||||||
|
frame = frame.replace('_prime', '')
|
||||||
|
row = Adw.ActionRow(
|
||||||
|
title=ID_TO_NAME[frame]
|
||||||
|
)
|
||||||
|
if not self._unique_rows.get(frame, False):
|
||||||
|
self._unique_rows[frame] = row
|
||||||
|
self.owned_frames_row.add_row(row)
|
||||||
|
for prime in self.app.profile.missing_primes:
|
||||||
|
row = Adw.ActionRow(
|
||||||
|
title=ID_TO_NAME[prime]
|
||||||
|
)
|
||||||
|
if not self._missing_primes_rows.get(prime, False):
|
||||||
|
self._missing_primes_rows[prime] = row
|
||||||
|
self.missing_primes_row.add_row(row)
|
||||||
|
for basic in self.app.profile.missing_basics:
|
||||||
|
row = Adw.ActionRow(
|
||||||
|
title=ID_TO_NAME[basic]
|
||||||
|
)
|
||||||
|
if not self._missing_basics_rows.get(basic, False):
|
||||||
|
self._missing_basics_rows[basic] = row
|
||||||
|
self.missing_basics_row.add_row(row)
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ class WishlistPage(Gtk.Box):
|
|||||||
wrapper.set_icon_name("logs-symbolic")
|
wrapper.set_icon_name("logs-symbolic")
|
||||||
self.btn_wishlist_add.connect('clicked', self._open_wishlist_dialogue)
|
self.btn_wishlist_add.connect('clicked', self._open_wishlist_dialogue)
|
||||||
self._wishlist_rows: dict[str, Adw.ActionRow] = {}
|
self._wishlist_rows: dict[str, Adw.ActionRow] = {}
|
||||||
|
self.refresh_wishlist()
|
||||||
|
|
||||||
def _open_wishlist_dialogue(self, button):
|
def _open_wishlist_dialogue(self, button):
|
||||||
dialog = FramePickerDialog(missing_frames=self.app.profile.missing_basics + self.app.profile.missing_primes)
|
dialog = FramePickerDialog(missing_frames=self.app.profile.missing_basics + self.app.profile.missing_primes, app=self.app)
|
||||||
dialog.connect('frame-selected', self.add_frame_to_wishlist)
|
dialog.connect('frame-selected', self.add_frame_to_wishlist)
|
||||||
dialog.present(self.get_root())
|
dialog.present(self.get_root())
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ class WishlistPage(Gtk.Box):
|
|||||||
self.wishlist.add(row)
|
self.wishlist.add(row)
|
||||||
self.app.profile.wishlist.append(frame_id)
|
self.app.profile.wishlist.append(frame_id)
|
||||||
self.app.profile.wishlist = sorted(self.app.profile.wishlist)
|
self.app.profile.wishlist = sorted(self.app.profile.wishlist)
|
||||||
self._refresh_wishlist
|
self.refresh_wishlist
|
||||||
|
|
||||||
def btn_remove(self, button):
|
def btn_remove(self, button):
|
||||||
self.remove_frame_from_wishlist(button.frame_id)
|
self.remove_frame_from_wishlist(button.frame_id)
|
||||||
@@ -61,14 +62,24 @@ class WishlistPage(Gtk.Box):
|
|||||||
if frame_id in self._wishlist_rows:
|
if frame_id in self._wishlist_rows:
|
||||||
row = self._wishlist_rows.pop(frame_id)
|
row = self._wishlist_rows.pop(frame_id)
|
||||||
self.wishlist.remove(row)
|
self.wishlist.remove(row)
|
||||||
|
if frame_id in self.app.profile.wishlist:
|
||||||
|
self.app.profile.wishlist.remove(frame_id)
|
||||||
|
|
||||||
def has_frame(self, frame_id) -> bool:
|
def has_frame(self, frame_id) -> bool:
|
||||||
return frame_id in self._wishlist_rows
|
return frame_id in self._wishlist_rows
|
||||||
|
|
||||||
def _refresh_wishlist(self):
|
def refresh_wishlist(self):
|
||||||
|
# deduplicate
|
||||||
|
self.app.profile.wishlist = list(set(self.app.profile.wishlist))
|
||||||
|
|
||||||
for frame_id in self.app.profile.wishlist:
|
for frame_id in self.app.profile.wishlist:
|
||||||
if frame_id not in self._wishlist_rows:
|
if frame_id not in self._wishlist_rows:
|
||||||
self.add_frame_to_wishlist(Adw.Dialog.new(),frame_id)
|
self.add_frame_to_wishlist(Adw.Dialog.new(),frame_id)
|
||||||
for frame_id in list(self._wishlist_rows):
|
to_delete: list[str] = []
|
||||||
|
for frame_id in self._wishlist_rows:
|
||||||
if frame_id not in self.app.profile.wishlist:
|
if frame_id not in self.app.profile.wishlist:
|
||||||
|
to_delete.append(frame_id)
|
||||||
|
if frame_id in self.app.profile.owned_frames:
|
||||||
|
to_delete.append(frame_id)
|
||||||
|
for frame_id in to_delete:
|
||||||
self.remove_frame_from_wishlist(frame_id)
|
self.remove_frame_from_wishlist(frame_id)
|
||||||
|
|||||||
@@ -5,22 +5,15 @@ from .constants import BASIC_WARFRAMES, PRIME_WARFRAMES
|
|||||||
# pseudo struct that contains the profile data that gets serialized into the .json profile.
|
# pseudo struct that contains the profile data that gets serialized into the .json profile.
|
||||||
@dataclass
|
@dataclass
|
||||||
class Profile:
|
class Profile:
|
||||||
owned_frames: dict[str, bool] = field(default_factory=dict)
|
owned_frames: list[str] = field(default_factory=list)
|
||||||
unique_frames_owned: int = 0
|
unique_frames_owned: int = 0
|
||||||
|
unique_frames_list: list[str] = field(default_factory=list)
|
||||||
missing_basics: list[str] = field(default_factory=list)
|
missing_basics: list[str] = field(default_factory=list)
|
||||||
missing_basics_count: int = BASIC_WARFRAMES
|
missing_basics_count: int = BASIC_WARFRAMES
|
||||||
missing_primes: list[str] = field(default_factory=list)
|
missing_primes: list[str] = field(default_factory=list)
|
||||||
missing_primes_count: int = PRIME_WARFRAMES
|
missing_primes_count: int = PRIME_WARFRAMES
|
||||||
wishlist: list[str] = field(default_factory=list)
|
wishlist: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
_on_change: Callable | None = field(default=None, repr=False, compare=False)
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
super().__setattr__(name, value)
|
|
||||||
# Notify on change (skip private attributes)
|
|
||||||
if not name.startswith('_') and hasattr(self, '_on_change') and self._on_change:
|
|
||||||
self._on_change()
|
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
"""Convert to dict, excluding private fields"""
|
"""Convert to dict, excluding private fields"""
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ class FramePickerDialog(Adw.Dialog):
|
|||||||
search_entry = Gtk.Template.Child()
|
search_entry = Gtk.Template.Child()
|
||||||
frame_list = Gtk.Template.Child()
|
frame_list = Gtk.Template.Child()
|
||||||
|
|
||||||
def __init__(self, missing_frames: list[str], **kwargs):
|
def __init__(self, app: Adw.ApplicationWindow, missing_frames: list[str], **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
self.app = app
|
||||||
self.missing_frames = missing_frames
|
self.missing_frames = missing_frames
|
||||||
self._populate_list()
|
self._populate_list()
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ class FramePickerDialog(Adw.Dialog):
|
|||||||
|
|
||||||
def _populate_list(self):
|
def _populate_list(self):
|
||||||
for frame_name in sorted(self.missing_frames):
|
for frame_name in sorted(self.missing_frames):
|
||||||
|
if frame_name not in self.app.profile.wishlist:
|
||||||
row = Adw.ActionRow(title=frame_name.replace("_", " ").title(),
|
row = Adw.ActionRow(title=frame_name.replace("_", " ").title(),
|
||||||
activatable=True
|
activatable=True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -56,4 +56,16 @@ class VoidmanifestWindow(Adw.ApplicationWindow):
|
|||||||
self.app.save_profile(self.app.profile)
|
self.app.save_profile(self.app.profile)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@Gtk.Template.Callback()
|
||||||
|
def on_page_changed(self, stack, param):
|
||||||
|
self.app.refresh_profile()
|
||||||
|
visible_child = stack.get_visible_child()
|
||||||
|
visible_name = stack.get_visible_child_name()
|
||||||
|
match visible_name:
|
||||||
|
case 'home':
|
||||||
|
visible_child.refresh()
|
||||||
|
case 'wishlist':
|
||||||
|
visible_child.refresh_wishlist()
|
||||||
|
case _:
|
||||||
|
return
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user