├── po
├── meson.build
└── LINGUAS
├── .rustfmt.toml
├── data
├── screenshots
│ ├── dark.png
│ └── light.png
├── resources
│ ├── style-hc-dark.css
│ ├── icons
│ │ └── scalable
│ │ │ ├── actions
│ │ │ ├── funnel-symbolic.svg
│ │ │ ├── get-symbolic.svg
│ │ │ ├── put-symbolic.svg
│ │ │ ├── arrow1-right-symbolic.svg
│ │ │ ├── regex-symbolic.svg
│ │ │ ├── memory-symbolic.svg
│ │ │ ├── merge-symbolic.svg
│ │ │ ├── pods-symbolic.svg
│ │ │ ├── code-symbolic.svg
│ │ │ ├── skull-symbolic.svg
│ │ │ ├── processor-symbolic.svg
│ │ │ ├── uppercase-symbolic.svg
│ │ │ ├── about-symbolic.svg
│ │ │ ├── success-small-symbolic.svg
│ │ │ ├── puzzle-piece-symbolic.svg
│ │ │ ├── stacked-plates-symbolic.svg
│ │ │ ├── cross-symbolic.svg
│ │ │ ├── whole-word-symbolic.svg
│ │ │ ├── bell-outline-symbolic.svg
│ │ │ ├── city-symbolic.svg
│ │ │ ├── eraser5-symbolic.svg
│ │ │ ├── build-configure-symbolic.svg
│ │ │ ├── ambulance-symbolic.svg
│ │ │ └── pip-out-symbolic.svg
│ │ │ └── status
│ │ │ ├── error-symbolic.svg
│ │ │ ├── success-symbolic.svg
│ │ │ ├── local-connection-symbolic.svg
│ │ │ └── verified-checkmark-symbolic.svg
│ ├── meson.build
│ ├── style-dark.css
│ ├── style-hc.css
│ └── resources.gresource.xml
├── resources.gresource.xml.in
├── icons
│ ├── meson.build
│ └── com.github.marhkb.Pods-symbolic.svg
├── com.github.marhkb.Pods.desktop.in.in
└── meson.build
├── .github
├── ISSUE_TEMPLATE
│ ├── blank_issue.md
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ └── issues.yml
├── src
├── podman.rs
├── widget
│ ├── property_widget_row.ui
│ ├── spinner.ui
│ ├── circular_progress_bar.ui
│ ├── random_name_entry_row.ui
│ ├── mod.rs
│ ├── main_menu_button.ui
│ ├── property_row.ui
│ ├── main_menu_button.rs
│ ├── zoom_control.ui
│ ├── source_view_search_widget.ui
│ ├── count_badge.ui
│ ├── text_search_entry.ui
│ ├── random_name_entry_row.rs
│ └── scalable_text_view.rs
├── config.rs.in
├── view
│ ├── value_row.ui
│ ├── container_renamer.ui
│ ├── repo_tag_simple_row.ui
│ ├── top_page_action_bar.ui
│ ├── containers_list_view.ui
│ ├── volumes_group.ui
│ ├── containers_group.ui
│ ├── actions_button.ui
│ ├── image_menu_button.ui
│ ├── containers_grid_view.ui
│ ├── image_pull_page.ui
│ ├── image_selection_combo_row.ui
│ ├── connection.rs
│ ├── container_health_check_log_row.ui
│ ├── key_val_row.ui
│ ├── info_row.ui
│ ├── pods_row.rs
│ ├── images_row.rs
│ ├── image_history_page.ui
│ ├── volumes_row.rs
│ ├── welcome_page.rs
│ ├── containers_row.rs
│ ├── top_page.ui
│ ├── action_row.ui
│ ├── container_volume_row.ui
│ ├── repo_tag_row.ui
│ ├── pod_menu_button.ui
│ ├── window.ui
│ ├── volume.rs
│ ├── images_row.ui
│ ├── volumes_row.ui
│ ├── container_menu_button.ui
│ ├── repo_tag_add_dialog.ui
│ ├── containers_count_bar.ui
│ ├── container_terminal_page.ui
│ ├── device_row.ui
│ ├── pods_prune_page.ui
│ ├── connections_sidebar.ui
│ ├── pods_prune_page.rs
│ ├── image.rs
│ ├── info_row.rs
│ ├── pods_row.ui
│ ├── image_search_response_row.ui
│ ├── containers_row.ui
│ ├── volume_creation_page.ui
│ ├── image_pull_page.rs
│ ├── containers_prune_page.ui
│ ├── volumes_prune_page.ui
│ ├── containers_list_view.rs
│ ├── container_renamer.rs
│ ├── volumes_prune_page.rs
│ └── containers_prune_page.rs
├── model
│ ├── pod_data.rs
│ ├── selectable.rs
│ ├── container_volume_list.rs
│ ├── value.rs
│ ├── health_check_log.rs
│ ├── key_val.rs
│ ├── image_data.rs
│ ├── container_volume.rs
│ ├── repo_tag.rs
│ ├── health_check_log_list.rs
│ ├── image_config.rs
│ ├── device.rs
│ ├── image_search_response.rs
│ └── port_mapping_list.rs
└── meson.build
├── .gitignore
├── meson_options.txt
├── .typos.toml
├── .editorconfig
├── Pods.doap
├── Cargo.toml
├── hooks
└── pre-commit.hook
└── meson.build
/po/meson.build:
--------------------------------------------------------------------------------
1 | i18n.gettext(gettext_package, preset: 'glib')
2 |
--------------------------------------------------------------------------------
/.rustfmt.toml:
--------------------------------------------------------------------------------
1 | imports_granularity = "Item"
2 | group_imports = "StdExternalCrate"
3 |
--------------------------------------------------------------------------------
/data/screenshots/dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marhkb/pods/HEAD/data/screenshots/dark.png
--------------------------------------------------------------------------------
/data/screenshots/light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marhkb/pods/HEAD/data/screenshots/light.png
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/blank_issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Blank Issue
3 | about: Create a blank issue.
4 | ---
5 |
--------------------------------------------------------------------------------
/src/podman.rs:
--------------------------------------------------------------------------------
1 | pub(crate) use podman_api::api;
2 | pub(crate) use podman_api::models;
3 | pub(crate) use podman_api::opts;
4 | pub(crate) use podman_api::*;
5 |
--------------------------------------------------------------------------------
/data/resources/style-hc-dark.css:
--------------------------------------------------------------------------------
1 | containercard separator {
2 | background-color: var(--border-color);
3 | }
4 |
5 | containercard.not-running {
6 | background-color: transparent;
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | build/
3 | _build/
4 | builddir/
5 | build-aux/app
6 | **/.flatpak-builder/
7 | src/config.rs
8 | *.ui.in~
9 | *.ui~
10 | .flatpak/
11 | vendor
12 | .vscode
13 | __pycache__/
14 |
--------------------------------------------------------------------------------
/po/LINGUAS:
--------------------------------------------------------------------------------
1 | ar
2 | ca
3 | da
4 | de
5 | es
6 | et
7 | eu
8 | fa
9 | fi
10 | fr
11 | he
12 | id
13 | it
14 | nb_NO
15 | nl
16 | oc
17 | pl
18 | pt
19 | pt_BR
20 | ro
21 | ru
22 | si
23 | sv
24 | ta
25 | tr
26 | uk
27 | zh_Hans
28 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/funnel-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/resources.gresource.xml.in:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @app-id@.metainfo.xml
5 |
6 |
7 |
--------------------------------------------------------------------------------
/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'profile',
3 | type: 'combo',
4 | choices: [
5 | 'default',
6 | 'development'
7 | ],
8 | value: 'default',
9 | description: 'The build profile for Pods. One of "default" or "development".'
10 | )
11 |
--------------------------------------------------------------------------------
/.typos.toml:
--------------------------------------------------------------------------------
1 | [type.po]
2 | extend-glob = ["*.po"]
3 | check-file = false
4 |
5 | [type.svg]
6 | extend-glob = ["*.svg"]
7 | check-file = false
8 |
9 | [default]
10 | extend-ignore-re = ["(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on"]
11 |
--------------------------------------------------------------------------------
/data/resources/meson.build:
--------------------------------------------------------------------------------
1 | # Resources
2 | resources = gnome.compile_resources(
3 | 'resources',
4 | 'resources.gresource.xml',
5 | gresource_bundle: true,
6 | source_dir: meson.current_build_dir(),
7 | install: true,
8 | install_dir: pkgdatadir,
9 | )
10 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/status/error-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/get-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/put-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/arrow1-right-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/icons/meson.build:
--------------------------------------------------------------------------------
1 | install_data(
2 | '@0@.svg'.format(application_id),
3 | install_dir: iconsdir / 'hicolor' / 'scalable' / 'apps'
4 | )
5 |
6 | install_data(
7 | '@0@-symbolic.svg'.format(base_id),
8 | install_dir: iconsdir / 'hicolor' / 'symbolic' / 'apps',
9 | rename: '@0@-symbolic.svg'.format(application_id)
10 | )
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest a new feature or improvement
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 | ---
8 |
9 |
11 |
12 | #### Describe your feature request
13 |
14 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/regex-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/icons/com.github.marhkb.Pods-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/memory-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/merge-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/pods-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/status/success-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/code-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 | [*]
3 | indent_style = space
4 | end_of_line = lf
5 | trim_trailing_whitespace = true
6 | insert_final_newline = true
7 | charset = utf-8
8 |
9 | [*.{build,css,doap,scss,ui,xml,xml.in,xml.in.in,yaml,yml}]
10 | indent_size = 2
11 |
12 | [*.{json,py,rs}]
13 | indent_size = 4
14 |
15 | [*.{c,h,h.in}]
16 | indent_size = 2
17 | max_line_length = 80
18 |
19 | [NEWS]
20 | indent_size = 2
21 | max_line_length = 72
22 |
--------------------------------------------------------------------------------
/data/com.github.marhkb.Pods.desktop.in.in:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=Pods
3 | Comment=Manage your Podman containers
4 | Type=Application
5 | Exec=pods
6 | Terminal=false
7 | Categories=GNOME;GTK;Development;System;Office;Network;Monitor;TerminalEmulator;RemoteAccess;
8 | Keywords=Gnome;GTK;libadwaita;Podman;Containerization;
9 | # Translators: Do NOT translate or transliterate this text (this is an icon file name)!
10 | Icon=@icon@
11 | StartupNotify=true
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 |
12 |
13 | ### Reproduction steps
14 |
15 | ### Environment
16 |
17 | - Pods version:
18 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/skull-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/style-dark.css:
--------------------------------------------------------------------------------
1 | containercard separator {
2 | background-color: var(--window-bg-color);
3 | }
4 |
5 | actionsbutton.finished .action-count-badge {
6 | background-color: var(--light-3);
7 | color: var(--dark-5);
8 | }
9 |
10 | .version,
11 | .container-health-status-not-running,
12 | .container-status-not-running,
13 | .image-unused,
14 | .pod-status-not-running,
15 | .volume-unused,
16 | .rich-expander-row-header #badge {
17 | color: var(--dark-3);
18 | background-color: var(--light-5);
19 | }
20 |
--------------------------------------------------------------------------------
/src/widget/property_widget_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | False
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/processor-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/widget/spinner.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 | center
8 | center
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/uppercase-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/widget/circular_progress_bar.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | center
5 | center
6 |
7 |
8 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/config.rs.in:
--------------------------------------------------------------------------------
1 | pub(crate) const APP_ID: &str = @APP_ID@;
2 | pub(crate) const GETTEXT_PACKAGE: &str = @GETTEXT_PACKAGE@;
3 | pub(crate) const LOCALEDIR: &str = @LOCALEDIR@;
4 | pub(crate) const PKGDATADIR: &str = @PKGDATADIR@;
5 | pub(crate) const PROFILE: &str = @PROFILE@;
6 | pub(crate) const RESOURCES_FILE: &str = concat!(@PKGDATADIR@, "/resources.gresource");
7 | pub(crate) const APPDATA_RESOURCES_FILE: &str = concat!(@PKGDATADIR@, "/appdata-resources.gresource");
8 | pub(crate) const UI_RESOURCES_FILE: &str = concat!(@PKGDATADIR@, "/ui-resources.gresource");
9 | pub(crate) const VERSION: &str = @VERSION@;
10 |
--------------------------------------------------------------------------------
/src/widget/random_name_entry_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/view/value_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Argument
5 |
6 |
7 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/about-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/success-small-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/view/container_renamer.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/puzzle-piece-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/view/repo_tag_simple_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | False
5 | False
6 | False
7 |
8 |
9 |
10 |
13 | 0
14 | True
15 | word-char
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/view/top_page_action_bar.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
15 | top-page.kill
16 | Kill
17 | center
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/stacked-plates-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/cross-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/whole-word-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/workflows/issues.yml:
--------------------------------------------------------------------------------
1 | name: Close inactive issues
2 | on:
3 | schedule:
4 | - cron: "30 1 * * *"
5 |
6 | jobs:
7 | close-issues:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | issues: write
11 | pull-requests: write
12 | steps:
13 | - uses: actions/stale@v8
14 | with:
15 | only-labels: "needs info"
16 | days-before-issue-stale: 30
17 | days-before-issue-close: 14
18 | stale-issue-label: "stale"
19 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
20 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
21 | days-before-pr-stale: -1
22 | days-before-pr-close: -1
23 | repo-token: ${{ secrets.GITHUB_TOKEN }}
24 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/bell-outline-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/view/containers_list_view.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | True
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 | none
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/city-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/eraser5-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/data/resources/style-hc.css:
--------------------------------------------------------------------------------
1 | containercard.not-running {
2 | background-color: var(--sidebar-bg-color);
3 | }
4 |
5 | .dim-icon {
6 | color: #ffffff;
7 | }
8 |
9 | .tag-label {
10 | font-weight: bold;
11 | }
12 |
13 | .container-health-status-healthy,
14 | .container-status-running,
15 | .image-used,
16 | .pod-status-running,
17 | .volume-used {
18 | background-color: var(--accent-bg-color);
19 | color: var(--accent-fg-color);
20 | }
21 |
22 | .pod-status-degraded {
23 | background-color: var(--accent-yellow);
24 | color: var(--accent-fg-color);
25 | }
26 |
27 | .container-status-dead,
28 | .container-status-unknown,
29 | .pod-status-error,
30 | .pod-status-dead,
31 | .pod-status-unknown,
32 | .container-health-status-unhealthy,
33 | .container-health-status-unknown {
34 | background-color: var(--error-bg-color);
35 | color: var(--error-fg-color);
36 | }
37 |
38 | .container-health-status-healthy {
39 | color: var(--accent-fg-color);
40 | background-color: var(--accent-green);
41 | }
42 |
--------------------------------------------------------------------------------
/src/view/volumes_group.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | volumes-group.create-volume
5 | False
6 |
7 |
8 |
9 | list-add-symbolic
10 | 15
11 | 15
12 | Create Volume
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 | none
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/build-configure-symbolic.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/view/containers_group.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | containers-group.create-container
5 | False
6 |
7 |
8 |
9 | list-add-symbolic
10 | 15
11 | 15
12 | Create Container
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 | none
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/widget/mod.rs:
--------------------------------------------------------------------------------
1 | mod circular_progress_bar;
2 | mod count_badge;
3 | mod date_time_row;
4 | mod main_menu_button;
5 | mod property_row;
6 | mod property_widget_row;
7 | mod random_name_entry_row;
8 | mod scalable_text_view;
9 | mod source_view_search_widget;
10 | mod spinner;
11 | mod text_search_entry;
12 | mod zoom_control;
13 |
14 | pub(crate) use self::circular_progress_bar::CircularProgressBar;
15 | pub(crate) use self::count_badge::CountBadge;
16 | pub(crate) use self::date_time_row::DateTimeRow;
17 | pub(crate) use self::main_menu_button::MainMenuButton;
18 | pub(crate) use self::property_row::PropertyRow;
19 | pub(crate) use self::property_widget_row::PropertyWidgetRow;
20 | pub(crate) use self::random_name_entry_row::RandomNameEntryRow;
21 | pub(crate) use self::scalable_text_view::ScalableTextView;
22 | pub(crate) use self::source_view_search_widget::SourceViewSearchWidget;
23 | pub(crate) use self::spinner::Spinner;
24 | pub(crate) use self::text_search_entry::TextSearchEntry;
25 | pub(crate) use self::zoom_control::ZoomControl;
26 |
--------------------------------------------------------------------------------
/src/view/actions_button.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | bell-outline-symbolic
20 |
21 |
22 |
23 |
24 |
25 | PdsActionsButton
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/view/image_menu_button.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
30 |
31 |
--------------------------------------------------------------------------------
/src/widget/main_menu_button.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Pods.doap:
--------------------------------------------------------------------------------
1 |
6 |
7 | Pods
8 | Manage your Podman containers
9 |
10 |
11 | Rust
12 |
13 |
14 |
15 | Marcus Behrendt
16 |
17 |
18 |
19 |
20 | marhkb
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/widget/property_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | False
8 |
9 |
10 |
11 |
14 | True
15 | right
16 | 3
17 | 3
18 | 9
19 | True
20 | True
21 | word-char
22 | 1
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/ambulance-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/view/containers_grid_view.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 21
16 | center
17 | True
18 | 5
19 | 21
20 | none
21 | start
22 |
23 | 30
24 | 30
25 | 36
26 | 36
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/view/image_pull_page.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Preferences
14 |
15 |
16 |
17 |
18 | True
19 | True
20 | _Download
21 |
22 | PdsImagePullPage
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/view/image_selection_combo_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Local Image
5 |
6 |
7 |
8 |
11 |
12 | container-creation-page.select-image
13 | computer-symbolic
14 | center
15 | Local Image
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 | container-creation-page.search-image
26 | network-server-symbolic
27 | center
28 | Remote Image
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/view/connection.rs:
--------------------------------------------------------------------------------
1 | use adw::prelude::*;
2 | use gettextrs::gettext;
3 | use gtk::glib;
4 |
5 | use crate::model;
6 | use crate::utils;
7 |
8 | pub(crate) fn show_ongoing_actions_warning_dialog>(
9 | widget: &W,
10 | connection_manager: &model::ConnectionManager,
11 | heading: &str,
12 | ) -> bool {
13 | if connection_manager
14 | .client()
15 | .map(|client| client.action_list().ongoing() > 0)
16 | .unwrap_or(false)
17 | {
18 | let dialog = adw::MessageDialog::builder()
19 | .heading(heading)
20 | .body_use_markup(true)
21 | .body(gettext(
22 | "There are ongoing actions whose progress will be irretrievably lost",
23 | ))
24 | .transient_for(&utils::root(widget))
25 | .build();
26 |
27 | dialog.add_responses(&[
28 | ("cancel", &gettext("_Cancel")),
29 | ("confirm", &gettext("_Confirm")),
30 | ]);
31 | dialog.set_default_response(Some("cancel"));
32 | dialog.set_response_appearance("confirm", adw::ResponseAppearance::Destructive);
33 |
34 | glib::MainContext::default().block_on(dialog.choose_future()) == "confirm"
35 | } else {
36 | true
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/widget/main_menu_button.rs:
--------------------------------------------------------------------------------
1 | use adw::subclass::prelude::*;
2 | use gtk::CompositeTemplate;
3 | use gtk::glib;
4 |
5 | use crate::utils;
6 |
7 | mod imp {
8 | use super::*;
9 |
10 | #[derive(Debug, Default, CompositeTemplate)]
11 | #[template(resource = "/com/github/marhkb/Pods/ui/widget/main_menu_button.ui")]
12 | pub(crate) struct MainMenuButton;
13 |
14 | #[glib::object_subclass]
15 | impl ObjectSubclass for MainMenuButton {
16 | const NAME: &'static str = "PdsMainMenuButton";
17 | type Type = super::MainMenuButton;
18 | type ParentType = gtk::Widget;
19 |
20 | fn class_init(klass: &mut Self::Class) {
21 | klass.bind_template();
22 | }
23 |
24 | fn instance_init(obj: &glib::subclass::InitializingObject) {
25 | obj.init_template();
26 | }
27 | }
28 |
29 | impl ObjectImpl for MainMenuButton {
30 | fn dispose(&self) {
31 | utils::unparent_children(&*self.obj());
32 | }
33 | }
34 |
35 | impl WidgetImpl for MainMenuButton {}
36 | }
37 |
38 | glib::wrapper! {
39 | pub(crate) struct MainMenuButton(ObjectSubclass)
40 | @extends gtk::Widget,
41 | @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
42 | }
43 |
--------------------------------------------------------------------------------
/src/widget/zoom_control.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 3
7 |
8 |
9 | 6
10 | 6
11 |
12 |
13 |
14 |
18 |
19 | zoom-out-symbolic
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 | zoom-original-symbolic
30 | 80
31 |
32 |
33 |
34 |
35 |
36 |
40 |
41 | zoom-in-symbolic
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/widget/source_view_search_widget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 | center
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | source-view-search-widget.search-backward
22 | go-up-symbolic
23 | Previous (Ctrl+Shift+G)
24 |
25 |
26 |
27 |
28 |
29 | source-view-search-widget.search-forward
30 | Next (Ctrl+G)
31 | go-down-symbolic
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "pods"
3 | version = "2.2.0"
4 | authors = ["Marcus Behrendt "]
5 | edition = "2024"
6 |
7 | [dependencies]
8 | astral-tokio-tar = { version = "0.5.6", default-features = false }
9 | adw = { version = "0.8", package = "libadwaita", features = ["v1_8"] }
10 | anyhow = "1"
11 | ashpd = { version = "0.12", default-features = false, features = ["gtk4", "tokio"] }
12 | futures = { version = "0.3", default-features = false }
13 | gettext-rs = { version = "=0.7.0", features = ["gettext-system"] }
14 | gtk = { version = "0.10", package = "gtk4", features = ["gnome_48"] }
15 | indexmap = { version = "2", features = ["serde"] }
16 | log = "0.4"
17 | multi_log = "0.1"
18 | names = { version = "0.14", default-features = false }
19 | oo7 = { version = "0.5", default-features = false, features = ["native_crypto", "tokio"] }
20 | paste = "1"
21 | podman-api = { git = "https://github.com/vv9k/podman-api-rs.git", rev = "363d945b9b9905c50dfa0bfe0f9331f9fdeef079", default-features = false }
22 | serde = "1"
23 | serde_json = "1"
24 | simplelog = { version = "0.12", features = ["paris"] }
25 | sourceview5 = { version = "0.10" }
26 | syslog = "7"
27 | tokio = "1"
28 | tokio-stream = { version = "0.1", default-features = false }
29 | vte = { version = "0.15", default-features = false }
30 | vte4 = "0.9"
31 |
32 | [profile.release]
33 | lto = true
34 | codegen-units = 1
35 |
--------------------------------------------------------------------------------
/src/widget/count_badge.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 | False
20 | center
21 | start
22 |
23 |
24 |
25 |
26 |
27 |
30 | False
31 | center
32 | start
33 |
34 |
35 |
36 | center
37 | center
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/model/pod_data.rs:
--------------------------------------------------------------------------------
1 | use std::cell::OnceCell;
2 |
3 | use glib::Properties;
4 | use glib::prelude::*;
5 | use glib::subclass::prelude::*;
6 | use gtk::glib;
7 |
8 | use crate::podman;
9 |
10 | mod imp {
11 | use super::*;
12 |
13 | #[derive(Debug, Default, Properties)]
14 | #[properties(wrapper_type = super::PodData)]
15 | pub(crate) struct PodData {
16 | #[property(get, set, construct_only)]
17 | pub(super) hostname: OnceCell,
18 | }
19 |
20 | #[glib::object_subclass]
21 | impl ObjectSubclass for PodData {
22 | const NAME: &'static str = "PodData";
23 | type Type = super::PodData;
24 | }
25 |
26 | impl ObjectImpl for PodData {
27 | fn properties() -> &'static [glib::ParamSpec] {
28 | Self::derived_properties()
29 | }
30 |
31 | fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
32 | self.derived_set_property(id, value, pspec);
33 | }
34 |
35 | fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
36 | self.derived_property(id, pspec)
37 | }
38 | }
39 | }
40 |
41 | glib::wrapper! {
42 | pub(crate) struct PodData(ObjectSubclass);
43 | }
44 |
45 | impl From<&podman::models::InspectPodData> for PodData {
46 | fn from(data: &podman::models::InspectPodData) -> Self {
47 | glib::Object::builder()
48 | .property("hostname", data.hostname.as_deref().unwrap_or_default())
49 | .build()
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/status/local-connection-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
42 |
--------------------------------------------------------------------------------
/src/view/container_health_check_log_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | False
12 |
13 |
14 |
15 |
16 | 100
17 | 300
18 | True
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 | False
29 | False
30 | True
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/view/key_val_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | False
5 | False
6 |
7 |
8 |
9 | True
10 | 18
11 | 12
12 | 12
13 | 18
14 | 9
15 |
16 |
17 |
18 |
19 | True
20 | Key
21 |
22 |
23 |
24 |
25 |
26 |
27 | True
28 | Value
29 |
30 |
31 |
32 |
33 |
34 |
37 | key-val-row.remove
38 | edit-delete-symbolic
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/model/selectable.rs:
--------------------------------------------------------------------------------
1 | use std::sync::OnceLock;
2 |
3 | use glib::prelude::*;
4 | use glib::subclass::prelude::*;
5 | use gtk::glib;
6 |
7 | mod imp {
8 | use super::*;
9 |
10 | #[allow(dead_code)]
11 | #[derive(Copy, Clone, Debug)]
12 | pub(crate) struct SelectableClass(glib::gobject_ffi::GTypeInterface);
13 |
14 | unsafe impl InterfaceStruct for SelectableClass {
15 | type Type = Selectable;
16 | }
17 |
18 | pub(crate) struct Selectable;
19 |
20 | #[glib::object_interface]
21 | impl ObjectInterface for Selectable {
22 | const NAME: &'static str = "Selectable";
23 | type Interface = SelectableClass;
24 |
25 | fn properties() -> &'static [glib::ParamSpec] {
26 | static PROPERTIES: OnceLock> = OnceLock::new();
27 | PROPERTIES.get_or_init(|| {
28 | vec![
29 | glib::ParamSpecBoolean::builder("selected")
30 | .explicit_notify()
31 | .build(),
32 | ]
33 | })
34 | }
35 | }
36 | }
37 |
38 | glib::wrapper! { pub(crate) struct Selectable(ObjectInterface); }
39 |
40 | pub(crate) trait SelectableExt: IsA {
41 | fn is_selected(&self) -> bool;
42 |
43 | fn set_selected(&self, value: bool);
44 |
45 | fn select(&self) {
46 | self.set_selected(!self.is_selected());
47 | }
48 | }
49 |
50 | impl> SelectableExt for T {
51 | fn is_selected(&self) -> bool {
52 | self.property("selected")
53 | }
54 |
55 | fn set_selected(&self, value: bool) {
56 | self.set_property("selected", value);
57 | }
58 | }
59 |
60 | unsafe impl IsImplementable for Selectable {}
61 |
--------------------------------------------------------------------------------
/src/view/info_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | 12
10 |
11 |
12 |
13 |
14 |
15 | about-symbolic
16 |
17 |
18 |
19 |
20 |
21 | end
22 | Info
23 | True
24 | 0
25 |
26 |
27 |
28 |
29 |
30 |
34 |
35 |
36 |
37 | loading
38 |
39 |
40 |
41 | image-loading-symbolic
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | version
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/data/resources/icons/scalable/actions/pip-out-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
44 |
--------------------------------------------------------------------------------
/src/view/pods_row.rs:
--------------------------------------------------------------------------------
1 | use adw::prelude::*;
2 | use adw::subclass::prelude::*;
3 | use glib::Properties;
4 | use gtk::CompositeTemplate;
5 | use gtk::glib;
6 |
7 | use crate::model;
8 | use crate::utils;
9 |
10 | mod imp {
11 | use super::*;
12 |
13 | #[derive(Debug, Default, Properties, CompositeTemplate)]
14 | #[properties(wrapper_type = super::PodsRow)]
15 | #[template(resource = "/com/github/marhkb/Pods/ui/view/pods_row.ui")]
16 | pub(crate) struct PodsRow {
17 | #[property(get, set)]
18 | pub(super) pod_list: glib::WeakRef,
19 | }
20 |
21 | #[glib::object_subclass]
22 | impl ObjectSubclass for PodsRow {
23 | const NAME: &'static str = "PdsPodsRow";
24 | type Type = super::PodsRow;
25 | type ParentType = gtk::Widget;
26 |
27 | fn class_init(klass: &mut Self::Class) {
28 | klass.bind_template();
29 | }
30 |
31 | fn instance_init(obj: &glib::subclass::InitializingObject) {
32 | obj.init_template();
33 | }
34 | }
35 |
36 | impl ObjectImpl for PodsRow {
37 | fn properties() -> &'static [glib::ParamSpec] {
38 | Self::derived_properties()
39 | }
40 |
41 | fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
42 | self.derived_set_property(id, value, pspec);
43 | }
44 |
45 | fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
46 | self.derived_property(id, pspec)
47 | }
48 |
49 | fn dispose(&self) {
50 | utils::unparent_children(&*self.obj());
51 | }
52 | }
53 |
54 | impl WidgetImpl for PodsRow {}
55 | }
56 |
57 | glib::wrapper! {
58 | pub(crate) struct PodsRow(ObjectSubclass)
59 | @extends gtk::Widget,
60 | @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
61 | }
62 |
--------------------------------------------------------------------------------
/src/view/images_row.rs:
--------------------------------------------------------------------------------
1 | use adw::prelude::*;
2 | use adw::subclass::prelude::*;
3 | use glib::Properties;
4 | use gtk::CompositeTemplate;
5 | use gtk::glib;
6 |
7 | use crate::model;
8 | use crate::utils;
9 |
10 | mod imp {
11 | use super::*;
12 |
13 | #[derive(Debug, Default, Properties, CompositeTemplate)]
14 | #[properties(wrapper_type = super::ImagesRow)]
15 | #[template(resource = "/com/github/marhkb/Pods/ui/view/images_row.ui")]
16 | pub(crate) struct ImagesRow {
17 | #[property(get, set)]
18 | pub(super) image_list: glib::WeakRef,
19 | }
20 |
21 | #[glib::object_subclass]
22 | impl ObjectSubclass for ImagesRow {
23 | const NAME: &'static str = "PdsImagesRow";
24 | type Type = super::ImagesRow;
25 | type ParentType = gtk::Widget;
26 |
27 | fn class_init(klass: &mut Self::Class) {
28 | klass.bind_template();
29 | }
30 |
31 | fn instance_init(obj: &glib::subclass::InitializingObject) {
32 | obj.init_template();
33 | }
34 | }
35 |
36 | impl ObjectImpl for ImagesRow {
37 | fn properties() -> &'static [glib::ParamSpec] {
38 | Self::derived_properties()
39 | }
40 |
41 | fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
42 | self.derived_set_property(id, value, pspec);
43 | }
44 |
45 | fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
46 | self.derived_property(id, pspec)
47 | }
48 |
49 | fn dispose(&self) {
50 | utils::unparent_children(&*self.obj());
51 | }
52 | }
53 |
54 | impl WidgetImpl for ImagesRow {}
55 | }
56 |
57 | glib::wrapper! {
58 | pub(crate) struct ImagesRow(ObjectSubclass)
59 | @extends gtk::Widget,
60 | @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
61 | }
62 |
--------------------------------------------------------------------------------
/src/view/image_history_page.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | center
29 | center
30 | 30
31 | 30
32 |
33 |
34 |
35 |
36 |
37 | loaded
38 |
39 |
40 |
41 |
42 |
43 |
44 | History List
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/view/volumes_row.rs:
--------------------------------------------------------------------------------
1 | use adw::prelude::*;
2 | use adw::subclass::prelude::*;
3 | use glib::Properties;
4 | use gtk::CompositeTemplate;
5 | use gtk::glib;
6 |
7 | use crate::model;
8 | use crate::utils;
9 |
10 | mod imp {
11 | use super::*;
12 |
13 | #[derive(Debug, Default, Properties, CompositeTemplate)]
14 | #[properties(wrapper_type = super::VolumesRow)]
15 | #[template(resource = "/com/github/marhkb/Pods/ui/view/volumes_row.ui")]
16 | pub(crate) struct VolumesRow {
17 | #[property(get, set)]
18 | pub(super) volume_list: glib::WeakRef,
19 | }
20 |
21 | #[glib::object_subclass]
22 | impl ObjectSubclass for VolumesRow {
23 | const NAME: &'static str = "PdsVolumesRow";
24 | type Type = super::VolumesRow;
25 | type ParentType = gtk::Widget;
26 |
27 | fn class_init(klass: &mut Self::Class) {
28 | klass.bind_template();
29 | }
30 |
31 | fn instance_init(obj: &glib::subclass::InitializingObject) {
32 | obj.init_template();
33 | }
34 | }
35 |
36 | impl ObjectImpl for VolumesRow {
37 | fn properties() -> &'static [glib::ParamSpec] {
38 | Self::derived_properties()
39 | }
40 |
41 | fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
42 | self.derived_set_property(id, value, pspec);
43 | }
44 |
45 | fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
46 | self.derived_property(id, pspec)
47 | }
48 |
49 | fn dispose(&self) {
50 | utils::unparent_children(&*self.obj());
51 | }
52 | }
53 |
54 | impl WidgetImpl for VolumesRow {}
55 | }
56 |
57 | glib::wrapper! {
58 | pub(crate) struct VolumesRow(ObjectSubclass)
59 | @extends gtk::Widget,
60 | @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
61 | }
62 |
--------------------------------------------------------------------------------
/src/view/welcome_page.rs:
--------------------------------------------------------------------------------
1 | use adw::prelude::*;
2 | use adw::subclass::prelude::*;
3 | use glib::Properties;
4 | use gtk::CompositeTemplate;
5 | use gtk::glib;
6 |
7 | use crate::model;
8 | use crate::utils;
9 |
10 | mod imp {
11 | use super::*;
12 |
13 | #[derive(Debug, Default, Properties, CompositeTemplate)]
14 | #[properties(wrapper_type = super::WelcomePage)]
15 | #[template(resource = "/com/github/marhkb/Pods/ui/view/welcome_page.ui")]
16 | pub(crate) struct WelcomePage {
17 | #[property(get, set, nullable)]
18 | pub(super) connection_manager: glib::WeakRef,
19 | }
20 |
21 | #[glib::object_subclass]
22 | impl ObjectSubclass for WelcomePage {
23 | const NAME: &'static str = "PdsWelcomePage";
24 | type Type = super::WelcomePage;
25 | type ParentType = gtk::Widget;
26 |
27 | fn class_init(klass: &mut Self::Class) {
28 | klass.bind_template();
29 | }
30 |
31 | fn instance_init(obj: &glib::subclass::InitializingObject) {
32 | obj.init_template();
33 | }
34 | }
35 |
36 | impl ObjectImpl for WelcomePage {
37 | fn properties() -> &'static [glib::ParamSpec] {
38 | Self::derived_properties()
39 | }
40 |
41 | fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
42 | self.derived_set_property(id, value, pspec);
43 | }
44 |
45 | fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
46 | self.derived_property(id, pspec)
47 | }
48 |
49 | fn dispose(&self) {
50 | utils::unparent_children(&*self.obj());
51 | }
52 | }
53 |
54 | impl WidgetImpl for WelcomePage {}
55 | }
56 |
57 | glib::wrapper! {
58 | pub(crate) struct WelcomePage(ObjectSubclass)
59 | @extends gtk::Widget,
60 | @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
61 | }
62 |
--------------------------------------------------------------------------------
/src/view/containers_row.rs:
--------------------------------------------------------------------------------
1 | use adw::prelude::*;
2 | use adw::subclass::prelude::*;
3 | use glib::Properties;
4 | use gtk::CompositeTemplate;
5 | use gtk::glib;
6 |
7 | use crate::model;
8 | use crate::utils;
9 |
10 | mod imp {
11 | use super::*;
12 |
13 | #[derive(Debug, Default, Properties, CompositeTemplate)]
14 | #[properties(wrapper_type = super::ContainersRow)]
15 | #[template(resource = "/com/github/marhkb/Pods/ui/view/containers_row.ui")]
16 | pub(crate) struct ContainersRow {
17 | #[property(get, set)]
18 | pub(super) container_list: glib::WeakRef,
19 | }
20 |
21 | #[glib::object_subclass]
22 | impl ObjectSubclass for ContainersRow {
23 | const NAME: &'static str = "PdsContainersRow";
24 | type Type = super::ContainersRow;
25 | type ParentType = gtk::Widget;
26 |
27 | fn class_init(klass: &mut Self::Class) {
28 | klass.bind_template();
29 | }
30 |
31 | fn instance_init(obj: &glib::subclass::InitializingObject) {
32 | obj.init_template();
33 | }
34 | }
35 |
36 | impl ObjectImpl for ContainersRow {
37 | fn properties() -> &'static [glib::ParamSpec] {
38 | Self::derived_properties()
39 | }
40 |
41 | fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
42 | self.derived_set_property(id, value, pspec);
43 | }
44 |
45 | fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
46 | self.derived_property(id, pspec)
47 | }
48 |
49 | fn dispose(&self) {
50 | utils::unparent_children(&*self.obj());
51 | }
52 | }
53 |
54 | impl WidgetImpl for ContainersRow {}
55 | }
56 |
57 | glib::wrapper! {
58 | pub(crate) struct ContainersRow(ObjectSubclass)
59 | @extends gtk::Widget,
60 | @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
61 | }
62 |
--------------------------------------------------------------------------------
/src/view/top_page.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | 28
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | True
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/model/container_volume_list.rs:
--------------------------------------------------------------------------------
1 | use std::cell::RefCell;
2 |
3 | use gio::prelude::*;
4 | use gio::subclass::prelude::*;
5 | use gtk::gio;
6 | use gtk::glib;
7 | use indexmap::map::IndexMap;
8 |
9 | use crate::model;
10 |
11 | mod imp {
12 | use super::*;
13 |
14 | #[derive(Debug, Default)]
15 | pub(crate) struct ContainerVolumeList(
16 | pub(super) RefCell>,
17 | );
18 |
19 | #[glib::object_subclass]
20 | impl ObjectSubclass for ContainerVolumeList {
21 | const NAME: &'static str = "ContainerVolumeList";
22 | type Type = super::ContainerVolumeList;
23 | type Interfaces = (gio::ListModel,);
24 | }
25 |
26 | impl ObjectImpl for ContainerVolumeList {}
27 |
28 | impl ListModelImpl for ContainerVolumeList {
29 | fn item_type(&self) -> glib::Type {
30 | model::ContainerVolume::static_type()
31 | }
32 |
33 | fn n_items(&self) -> u32 {
34 | self.0.borrow().len() as u32
35 | }
36 |
37 | fn item(&self, position: u32) -> Option {
38 | self.0
39 | .borrow()
40 | .get_index(position as usize)
41 | .map(|(_, obj)| obj.clone().upcast())
42 | }
43 | }
44 | }
45 |
46 | glib::wrapper! {
47 | pub(crate) struct ContainerVolumeList(ObjectSubclass)
48 | @implements gio::ListModel;
49 | }
50 |
51 | impl Default for ContainerVolumeList {
52 | fn default() -> Self {
53 | glib::Object::builder().build()
54 | }
55 | }
56 |
57 | impl ContainerVolumeList {
58 | pub(crate) fn add_volume(&self, container_volume: model::ContainerVolume) {
59 | if let Some(ref volume) = container_volume.volume() {
60 | let (index, _) = self
61 | .imp()
62 | .0
63 | .borrow_mut()
64 | .insert_full(volume.inner().name.clone(), container_volume);
65 |
66 | self.items_changed(index as u32, 0, 1);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/view/action_row.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 6
7 |
8 |
9 |
10 |
11 |
12 | type
13 | center
14 |
15 |
16 |
17 |
18 |
19 | vertical
20 | center
21 |
22 |
23 |
24 | end
25 | True
26 | True
27 | true
28 | True
29 | 0.0
30 |
31 |
32 |
33 |
34 |
35 |
38 | end
39 | True
40 | True
41 | True
42 | 0.0
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
54 | action
55 | client-view.cancel-or-delete-action
56 | center
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/model/value.rs:
--------------------------------------------------------------------------------
1 | use std::cell::RefCell;
2 | use std::sync::OnceLock;
3 |
4 | use glib::Properties;
5 | // use gtk::glib::subclass::Signal;
6 | use glib::prelude::*;
7 | use glib::subclass::Signal;
8 | use glib::subclass::prelude::*;
9 | use gtk::glib;
10 |
11 | mod imp {
12 | use super::*;
13 |
14 | #[derive(Debug, Default, Properties)]
15 | #[properties(wrapper_type = super::Value)]
16 | pub(crate) struct Value {
17 | #[property(name = "value", get, set)]
18 | pub(super) inner: RefCell,
19 | }
20 |
21 | #[glib::object_subclass]
22 | impl ObjectSubclass for Value {
23 | const NAME: &'static str = "Value";
24 | type Type = super::Value;
25 | }
26 |
27 | impl ObjectImpl for Value {
28 | fn signals() -> &'static [Signal] {
29 | static SIGNALS: OnceLock> = OnceLock::new();
30 | SIGNALS.get_or_init(|| vec![Signal::builder("remove-request").build()])
31 | }
32 |
33 | fn properties() -> &'static [glib::ParamSpec] {
34 | Self::derived_properties()
35 | }
36 |
37 | fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
38 | self.derived_set_property(id, value, pspec);
39 | }
40 |
41 | fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
42 | self.derived_property(id, pspec)
43 | }
44 | }
45 | }
46 |
47 | glib::wrapper! {
48 | pub(crate) struct Value(ObjectSubclass);
49 | }
50 |
51 | impl Default for Value {
52 | fn default() -> Self {
53 | glib::Object::builder().build()
54 | }
55 | }
56 |
57 | impl Value {
58 | pub(crate) fn remove_request(&self) {
59 | self.emit_by_name::<()>("remove-request", &[]);
60 | }
61 |
62 | pub(crate) fn connect_remove_request(
63 | &self,
64 | f: F,
65 | ) -> glib::SignalHandlerId {
66 | self.connect_local("remove-request", true, move |values| {
67 | let obj = values[0].get::().unwrap();
68 | f(&obj);
69 |
70 | None
71 | })
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/model/health_check_log.rs:
--------------------------------------------------------------------------------
1 | use std::cell::OnceCell;
2 |
3 | use glib::Properties;
4 | use glib::prelude::*;
5 | use glib::subclass::prelude::*;
6 | use gtk::gio;
7 | use gtk::glib;
8 |
9 | use crate::model;
10 | use crate::podman;
11 |
12 | mod imp {
13 | use super::*;
14 |
15 | #[derive(Debug, Default, Properties)]
16 | #[properties(wrapper_type = super::HealthCheckLog)]
17 | pub(crate) struct HealthCheckLog {
18 | #[property(get, set, construct_only)]
19 | pub(super) end: OnceCell,
20 | #[property(get, set, construct_only)]
21 | pub(super) exit_code: OnceCell,
22 | #[property(get, set, construct_only)]
23 | pub(super) output: OnceCell,
24 | #[property(get, set, construct_only)]
25 | pub(super) start: OnceCell,
26 | }
27 |
28 | #[glib::object_subclass]
29 | impl ObjectSubclass for HealthCheckLog {
30 | const NAME: &'static str = "HealthCheckLog";
31 | type Type = super::HealthCheckLog;
32 | }
33 |
34 | impl ObjectImpl for HealthCheckLog {
35 | fn properties() -> &'static [glib::ParamSpec] {
36 | Self::derived_properties()
37 | }
38 |
39 | fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
40 | self.derived_set_property(id, value, pspec);
41 | }
42 |
43 | fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
44 | self.derived_property(id, pspec)
45 | }
46 | }
47 | }
48 |
49 | glib::wrapper! {
50 | pub(crate) struct HealthCheckLog(ObjectSubclass)
51 | @implements gio::ListModel, model::SelectableList;
52 | }
53 |
54 | impl From<&podman::models::HealthCheckLog> for HealthCheckLog {
55 | fn from(data: &podman::models::HealthCheckLog) -> Self {
56 | glib::Object::builder()
57 | .property("end", data.end.as_ref().unwrap())
58 | .property("exit-code", data.exit_code.unwrap())
59 | .property("output", data.output.as_ref().unwrap())
60 | .property("start", data.start.as_ref().unwrap())
61 | .build()
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/hooks/pre-commit.hook:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Source: https://gitlab.gnome.org/GNOME/fractal/blob/master/hooks/pre-commit.hook
3 |
4 | install_rustfmt() {
5 | if ! which rustup &> /dev/null; then
6 | curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain none -y
7 | export PATH=$PATH:$HOME/.cargo/bin
8 | if ! which rustup &> /dev/null; then
9 | echo "Failed to install rustup. Performing the commit without style checking."
10 | exit 0
11 | fi
12 | fi
13 |
14 | if ! rustup toolchain list | grep nightly &> /dev/null; then
15 | echo "Installing minimal rust nightly toolchain…"
16 | rustup toolchain install nightly --profile minimal
17 | fi
18 |
19 | if ! rustup component list --toolchain=nightly | grep rustfmt &> /dev/null; then
20 | echo "Installing rustfmt…"
21 | rustup component add rustfmt --toolchain=nightly
22 | fi
23 | }
24 |
25 | if ! which cargo >/dev/null 2>&1 || ! cargo +nightly fmt --help >/dev/null 2>&1; then
26 | echo "Unable to check the project’s code style, because rustfmt could not be run."
27 |
28 | if [ ! -t 1 ]; then
29 | # No input is possible
30 | echo "Performing commit."
31 | exit 0
32 | fi
33 |
34 | echo ""
35 | echo "y: Install rustfmt via rustup"
36 | echo "n: Don't install rustfmt and perform the commit"
37 | echo "Q: Don't install rustfmt and abort the commit"
38 |
39 | echo ""
40 | while true
41 | do
42 | echo -n "Install rustfmt via rustup? [y/n/Q]: "; read yn < /dev/tty
43 | case $yn in
44 | [Yy]* ) install_rustfmt; break;;
45 | [Nn]* ) echo "Performing commit."; exit 0;;
46 | [Qq]* | "" ) echo "Aborting commit."; exit -1 >/dev/null 2>&1;;
47 | * ) echo "Invalid input";;
48 | esac
49 | done
50 |
51 | fi
52 |
53 | echo "--Checking style--"
54 | cargo +nightly fmt --all -- --check
55 | if test $? != 0; then
56 | echo "--Checking style fail--"
57 | echo "Please fix the above issues, either manually or by running: cargo +nightly fmt --all"
58 |
59 | exit -1
60 | else
61 | echo "--Checking style pass--"
62 | fi
63 |
--------------------------------------------------------------------------------
/src/model/key_val.rs:
--------------------------------------------------------------------------------
1 | use std::cell::RefCell;
2 | use std::sync::OnceLock;
3 |
4 | use glib::Properties;
5 | use glib::prelude::*;
6 | use glib::subclass::Signal;
7 | use glib::subclass::prelude::*;
8 | use gtk::glib;
9 |
10 | mod imp {
11 | use super::*;
12 |
13 | #[derive(Debug, Default, Properties)]
14 | #[properties(wrapper_type = super::KeyVal)]
15 | pub(crate) struct KeyVal {
16 | #[property(get, set)]
17 | pub(super) key: RefCell,
18 | #[property(get, set)]
19 | pub(super) value: RefCell,
20 | }
21 |
22 | #[glib::object_subclass]
23 | impl ObjectSubclass for KeyVal {
24 | const NAME: &'static str = "KeyVal";
25 | type Type = super::KeyVal;
26 | }
27 |
28 | impl ObjectImpl for KeyVal {
29 | fn signals() -> &'static [Signal] {
30 | static SIGNALS: OnceLock> = OnceLock::new();
31 | SIGNALS.get_or_init(|| vec![Signal::builder("remove-request").build()])
32 | }
33 |
34 | fn properties() -> &'static [glib::ParamSpec] {
35 | Self::derived_properties()
36 | }
37 |
38 | fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
39 | self.derived_set_property(id, value, pspec);
40 | }
41 |
42 | fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
43 | self.derived_property(id, pspec)
44 | }
45 | }
46 | }
47 |
48 | glib::wrapper! {
49 | pub(crate) struct KeyVal(ObjectSubclass);
50 | }
51 |
52 | impl Default for KeyVal {
53 | fn default() -> Self {
54 | glib::Object::builder().build()
55 | }
56 | }
57 |
58 | impl KeyVal {
59 | pub(crate) fn remove_request(&self) {
60 | self.emit_by_name::<()>("remove-request", &[]);
61 | }
62 |
63 | pub(crate) fn connect_remove_request(
64 | &self,
65 | f: F,
66 | ) -> glib::SignalHandlerId {
67 | self.connect_local("remove-request", true, move |values| {
68 | let obj = values[0].get::().unwrap();
69 | f(&obj);
70 |
71 | None
72 | })
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/model/image_data.rs:
--------------------------------------------------------------------------------
1 | use std::cell::OnceCell;
2 |
3 | use glib::Properties;
4 | use glib::prelude::*;
5 | use glib::subclass::prelude::*;
6 | use gtk::glib;
7 |
8 | use crate::model;
9 | use crate::podman;
10 |
11 | mod imp {
12 | use super::*;
13 |
14 | #[derive(Debug, Default, Properties)]
15 | #[properties(wrapper_type = super::ImageData)]
16 | pub(crate) struct ImageData {
17 | #[property(get, set, construct_only, nullable)]
18 | pub(super) architecture: OnceCell