Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package waycheck for openSUSE:Factory checked in at 2023-10-16 22:33:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/waycheck (Old) and /work/SRC/openSUSE:Factory/.waycheck.new.20540 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "waycheck" Mon Oct 16 22:33:15 2023 rev:3 rq:1117907 version:1.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/waycheck/waycheck.changes 2023-10-01 21:23:21.602933188 +0200 +++ /work/SRC/openSUSE:Factory/.waycheck.new.20540/waycheck.changes 2023-10-16 22:33:28.992901202 +0200 @@ -1,0 +2,13 @@ +Sun Oct 15 20:37:38 UTC 2023 - Neal Gompa <ngo...@opensuse.org> + +- Update to v1.0.0 + + Add a tab for ChromeOS protocols + + Add a tab for unknown protocol interfaces + + Add a search bar for quickly finding protocols + + Add a combo box to filter by implemented or unimplemented protocols + + Add explanatory tooltips to table headers + + Fix detection of the virtual pointer manager protocol + + Fix detection of the DRM lease protocol + + Fix warning on startup from the Qt desktop filename + +------------------------------------------------------------------- Old: ---- waycheck-v0.2.0.tar.gz New: ---- waycheck-v1.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ waycheck.spec ++++++ --- /var/tmp/diff_new_pack.rmxhpl/_old 2023-10-16 22:33:30.368950836 +0200 +++ /var/tmp/diff_new_pack.rmxhpl/_new 2023-10-16 22:33:30.372950980 +0200 @@ -19,7 +19,7 @@ %global qt6_minver 6.5 Name: waycheck -Version: 0.2.0 +Version: 1.0.0 Release: 0 Summary: GUI that displays protocols implemented by a Wayland compositor ++++++ waycheck-v0.2.0.tar.gz -> waycheck-v1.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/.gitignore new/waycheck-v1.0.0/.gitignore --- old/waycheck-v0.2.0/.gitignore 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/.gitignore 2023-10-15 20:51:45.000000000 +0200 @@ -7,4 +7,5 @@ justfile /.fleet/ /.idea/ +/.vscode/ /.cache/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/.gitlab-ci.yml new/waycheck-v1.0.0/.gitlab-ci.yml --- old/waycheck-v0.2.0/.gitlab-ci.yml 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/.gitlab-ci.yml 2023-10-15 20:51:45.000000000 +0200 @@ -9,7 +9,7 @@ - meson setup build - meson compile -C build rules: - - if: $CI_PIPELINE_SOURCE == "push" + - if: '$CI_PROJECT_NAMESPACE == "serebit" && $CI_PIPELINE_SOURCE == "push"' .build_rpmdistro: &rpmdistrobuild stage: build @@ -18,7 +18,7 @@ - meson setup build - meson compile -C build rules: - - if: $CI_PIPELINE_SOURCE == "push" + - if: '$CI_PROJECT_NAMESPACE == "serebit" && $CI_PIPELINE_SOURCE == "push"' build-job-fedora: image: quay.io/fedora/fedora:latest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/README.md new/waycheck-v1.0.0/README.md --- old/waycheck-v0.2.0/README.md 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/README.md 2023-10-15 20:51:45.000000000 +0200 @@ -1,6 +1,16 @@ # Waycheck -Waycheck is a simple Qt6 application that displays all protocols implemented by the compositor that it's running in. +Waycheck is a simple Qt6 application that displays all of the Wayland protocols that your compositor supports, and all of the protocols that it doesn't support. It can be used to compare protocol support between compositors, or if you're working on your own compositor, to keep track of which protocols you still need to implement. + +## Installation + +### Distro Packages + +[](https://repology.org/project/waycheck/versions) + +### Flatpak + +[](https://flathub.org/apps/dev.serebit.Waycheck) ## License diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/meson.build new/waycheck-v1.0.0/meson.build --- old/waycheck-v0.2.0/meson.build 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/meson.build 2023-10-15 20:51:45.000000000 +0200 @@ -1,7 +1,7 @@ project( 'waycheck', 'cpp', - version: '0.2.0', + version: '1.0.0', license: 'Apache-2.0', meson_version: '>= 0.59.0', default_options: [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/resources/dev.serebit.Waycheck.metainfo.xml new/waycheck-v1.0.0/resources/dev.serebit.Waycheck.metainfo.xml --- old/waycheck-v0.2.0/resources/dev.serebit.Waycheck.metainfo.xml 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/resources/dev.serebit.Waycheck.metainfo.xml 2023-10-15 20:51:45.000000000 +0200 @@ -4,7 +4,7 @@ <id>dev.serebit.Waycheck</id> <metadata_license>CC0-1.0</metadata_license> <project_license>Apache-2.0</project_license> - + <name>Waycheck</name> <summary>Displays the protocols implemented by a Wayland compositor</summary> <description> @@ -60,6 +60,20 @@ </screenshots> <releases> + <release version="1.0.0" date="2023-10-15"> + <description> + <ul> + <li>Add a tab for ChromeOS protocols</li> + <li>Add a tab for unknown protocol interfaces</li> + <li>Add a search bar for quickly finding protocols</li> + <li>Add a combo box to filter by implemented or unimplemented protocols</li> + <li>Add explanatory tooltips to table headers</li> + <li>Fix detection of the virtual pointer manager protocol</li> + <li>Fix detection of the DRM lease protocol</li> + <li>Fix warning on startup from the Qt desktop filename</li> + </ul> + </description> + </release> <release version="0.2.0" date="2023-09-28"> <description> <ul> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/src/main.cpp new/waycheck-v1.0.0/src/main.cpp --- old/waycheck-v0.2.0/src/main.cpp 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/src/main.cpp 2023-10-15 20:51:45.000000000 +0200 @@ -6,7 +6,7 @@ int main(int argc, char** argv) { auto app = QApplication(argc, argv); - app.setDesktopFileName(QStringLiteral("dev.serebit.Waycheck.desktop")); + app.setDesktopFileName(QStringLiteral("dev.serebit.Waycheck")); auto waylandApp = qApp->nativeInterface<QNativeInterface::QWaylandApplication>(); QMessageBox msgBox; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/src/meson.build new/waycheck-v1.0.0/src/meson.build --- old/waycheck-v0.2.0/src/meson.build 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/src/meson.build 2023-10-15 20:51:45.000000000 +0200 @@ -1,12 +1,13 @@ window_ui = qt6.preprocess( ui_files: ['window.ui'], - moc_headers: ['window.hpp'], + moc_headers: ['window.hpp', 'model.hpp'], dependencies: [dep_qt6], ) waycheck_sources = [ 'main.cpp', 'protocols.cpp', + 'model.cpp', 'window.cpp', window_ui, ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/src/model.cpp new/waycheck-v1.0.0/src/model.cpp --- old/waycheck-v0.2.0/src/model.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/waycheck-v1.0.0/src/model.cpp 2023-10-15 20:51:45.000000000 +0200 @@ -0,0 +1,62 @@ +#include "model.hpp" +#include "protocols.hpp" + +Model::Model(const ProtocolSource source) + : searchModel(), supportModel(), searchFilter(""), supportFilter(ALL), sourceModel(), filteredModel(&supportModel), + name(QString::fromStdString(source_to_string(source))), includeStatus(source == UPSTREAM), + includeName(source != UNKNOWN) { + searchModel.setSourceModel(&sourceModel); + searchModel.setFilterKeyColumn(-1); + searchModel.setFilterCaseSensitivity(Qt::CaseInsensitive); + + supportModel.setSourceModel(&searchModel); + supportModel.setFilterRole(Qt::UserRole); + + if (includeStatus) { + sourceModel.setHorizontalHeaderLabels({"Status", "Name", "Identifier", "Interface Version"}); + sourceModel.horizontalHeaderItem(0)->setToolTip("The stability level of the protocol"); + sourceModel.horizontalHeaderItem(1)->setToolTip("The human-readable name of the protocol"); + sourceModel.horizontalHeaderItem(2)->setToolTip("The full name of the protocol's upstream XML representation"); + sourceModel.horizontalHeaderItem(3)->setToolTip( + "The version of the protocol's interface that's implemented by the compositor"); + } else if (includeName) { + sourceModel.setHorizontalHeaderLabels({"Name", "Identifier", "Interface Version"}); + sourceModel.horizontalHeaderItem(0)->setToolTip("The human-readable name of the protocol"); + sourceModel.horizontalHeaderItem(1)->setToolTip("The full name of the protocol's upstream XML representation"); + sourceModel.horizontalHeaderItem(2)->setToolTip( + "The version of the protocol's interface that's implemented by the compositor"); + } else { + sourceModel.setHorizontalHeaderLabels({"Interface Identifier", "Interface Version"}); + sourceModel.horizontalHeaderItem(0)->setToolTip("The identifier reported by the compositor for this interface"); + sourceModel.horizontalHeaderItem(1)->setToolTip("The version of the interface that's implemented by the compositor"); + } +} + +Model::~Model() = default; + +void Model::setSearchFilter(const QString& searchFilter) { + this->searchFilter = searchFilter; + searchModel.setFilterFixedString(searchFilter); + updateFilter(); +} + +void Model::setSupportFilter(const supportFilters supportFilter) { + this->supportFilter = supportFilter; + switch (supportFilter) { + default: + case ALL: + supportModel.setFilterFixedString(""); + break; + case SUPPORTED: + supportModel.setFilterFixedString("true"); + break; + case UNSUPPORTED: + supportModel.setFilterFixedString("false"); + break; + } + updateFilter(); +} + +void Model::updateFilter() { + emit filterChanged(!searchFilter.isEmpty() || !(supportFilter == ALL)); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/src/model.hpp new/waycheck-v1.0.0/src/model.hpp --- old/waycheck-v0.2.0/src/model.hpp 1970-01-01 01:00:00.000000000 +0100 +++ new/waycheck-v1.0.0/src/model.hpp 2023-10-15 20:51:45.000000000 +0200 @@ -0,0 +1,45 @@ +#ifndef MODEL_HPP +#define MODEL_HPP + +#include "protocols.hpp" + +#include <QSortFilterProxyModel> +#include <QStandardItemModel> + +class Model : public QObject { + Q_OBJECT + + public: + enum supportFilters { + ALL, + SUPPORTED, + UNSUPPORTED + }; + + private: + QSortFilterProxyModel searchModel; + QSortFilterProxyModel supportModel; + QString searchFilter; + supportFilters supportFilter; + + public: + Model(ProtocolSource source); + ~Model(); + + QStandardItemModel sourceModel; + QSortFilterProxyModel* const filteredModel; + QString const name; + bool const includeStatus; + bool const includeName; + + void setSearchFilter(const QString& searchFilter); + + void setSupportFilter(const supportFilters supportFilter); + + void updateFilter(); + + signals: + void filterChanged(bool filtering); +}; + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/src/protocols.cpp new/waycheck-v1.0.0/src/protocols.cpp --- old/waycheck-v0.2.0/src/protocols.cpp 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/src/protocols.cpp 2023-10-15 20:51:45.000000000 +0200 @@ -1,5 +1,22 @@ #include "protocols.hpp" +std::string source_to_string(ProtocolSource source) { + switch (source) { + case UPSTREAM: + return "Upstream"; + case WLROOTS: + return "Wlroots"; + case KDE: + return "KDE"; + case WESTON: + return "Weston"; + case CHROMEOS: + return "ChromeOS"; + default: + return "Unknown"; + } +} + std::string status_to_string(ProtocolStatus status) { switch (status) { case STABLE: @@ -8,13 +25,7 @@ return "Staging"; case UNSTABLE: return "Unstable"; - case WLROOTS: - return "Wlroots"; - case KDE: - return "KDE"; - case WESTON: - return "Weston"; default: - return "Unknown"; + return "None"; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/src/protocols.hpp new/waycheck-v1.0.0/src/protocols.hpp --- old/waycheck-v0.2.0/src/protocols.hpp 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/src/protocols.hpp 2023-10-15 20:51:45.000000000 +0200 @@ -5,16 +5,23 @@ #include <unordered_map> enum ProtocolStatus { - UNKNOWN, STABLE, STAGING, UNSTABLE, + NONE, +}; + +enum ProtocolSource { + UNKNOWN, + UPSTREAM, WLROOTS, KDE, WESTON, + CHROMEOS, }; struct Protocol { + ProtocolSource source; ProtocolStatus status; std::string pretty; std::string name; @@ -22,87 +29,93 @@ // clang-format off static std::unordered_map<std::string, const Protocol> known_protocols = { - {"wp_presentation", {STABLE, "Presentation time", "presentation-time"}}, - {"wp_viewporter", {STABLE, "Viewporter", "viewporter"}}, - {"xdg_wm_base", {STABLE, "XDG shell", "xdg-shell"}}, - - {"xdg_activation_v1", {STAGING, "XDG activation", "xdg-activation-v1"}}, - {"wp_drm_lease_v1", {STAGING, "DRM lease", "drm-lease-v1"}}, - {"ext_session_lock_manager_v1", {STAGING, "Session lock", "ext-session-lock-v1"}}, - {"wp_single_pixel_buffer_manager_v1", {STAGING, "Single-pixel buffer", "single-pixel-buffer-v1"}}, - {"wp_content_type_manager_v1", {STAGING, "Content type hint", "content-type-v1"}}, - {"ext_idle_notifier_v1", {STAGING, "Idle notify", "ext-idle-notify-v1"}}, - {"wp_tearing_control_manager_v1", {STAGING, "Tearing control", "tearing-control-v1"}}, - {"xwayland_shell_v1", {STAGING, "Xwayland shell", "xwayland-shell-v1"}}, - {"wp_fractional_scale_manager_v1", {STAGING, "Fractional scale", "fractional-scale-v1"}}, - {"wp_cursor_shape_manager_v1", {STAGING, "Cursor shape", "cursor-shape-v1"}}, - {"ext_foreign_toplevel_list_v1", {STAGING, "Foreign toplevel list", "ext-foreign-toplevel-list-v1"}}, - {"wp_security_context_manager_v1", {STAGING, "Security context", "security-context-v1"}}, - - {"zwp_fullscreen_shell_v1", {UNSTABLE, "Fullscreen shell", "fullscreen-shell-unstable-v1"}}, - {"zwp_idle_inhibit_manager_v1", {UNSTABLE, "Idle inhibit", "idle-inhibit-unstable-v1"}}, - {"zwp_input_method_context_v1", {UNSTABLE, "Input method", "input-method-unstable-v1"}}, - {"zwp_input_timestamps_manager_v1", {UNSTABLE, "Input timestamps", "input-timestamps-unstable-v1"}}, - {"zwp_keyboard_shortcuts_inhibit_manager_v1", {UNSTABLE, "Keyboard shortcuts inhibit", "keyboard-shortcuts-inhibit-unstable-v1"}}, - {"zwp_linux_dmabuf_v1", {UNSTABLE, "Linux DMA-BUF", "linux-dmabuf-unstable-v1"}}, - {"zwp_linux_explicit_synchronization_v1", {UNSTABLE, "Linux explicit synchronization", "linux-explicit-synchronization-unstable-v1"}}, - {"zwp_pointer_constraints_v1", {UNSTABLE, "Pointer constraints", "pointer-constraints-unstable-v1"}}, - {"zwp_pointer_gestures_v1", {UNSTABLE, "Pointer gestures", "pointer-gestures-unstable-v1"}}, - {"zwp_primary_selection_device_manager_v1", {UNSTABLE, "Primary selection", "primary-selection-unstable-v1"}}, - {"zwp_relative_pointer_manager_v1", {UNSTABLE, "Relative pointer", "relative-pointer-unstable-v1"}}, - {"zwp_tablet_manager_v2", {UNSTABLE, "Tablet", "tablet-unstable-v2"}}, - {"zwp_text_input_v3", {UNSTABLE, "Text input", "text-input-unstable-v3"}}, - {"zxdg_decoration_manager_v1", {UNSTABLE, "XDG decoration", "xdg-decoration-unstable-v1"}}, - {"zxdg_exporter_v2", {UNSTABLE, "XDG foreign", "xdg-foreign-unstable-v2"}}, - {"zxdg_output_manager_v1", {UNSTABLE, "XDG output", "xdg-output-unstable-v1"}}, - {"zwp_xwayland_keyboard_grab_manager_v1", {UNSTABLE, "Xwayland keyboard grabbing", "xwayland-keyboard-grab-unstable-v1"}}, - - {"zwlr_data_control_manager_v1", {WLROOTS, "Data control", "wlr-data-control-unstable-v1"}}, - {"zwlr_export_dmabuf_manager_v1", {WLROOTS, "Export DMA-BUF", "wlr-export-dmabuf-unstable-v1"}}, - {"zwlr_foreign_toplevel_manager_v1", {WLROOTS, "Foreign toplevel management", "wlr-foreign-toplevel-management-unstable-v1"}}, - {"zwlr_gamma_control_manager_v1", {WLROOTS, "Gamma control", "wlr-gamma-control-unstable-v1"}}, - {"zwlr_input_inhibit_manager_v1", {WLROOTS, "Input inhibitor", "wlr-input-inhibitor-unstable-v1"}}, - {"zwlr_layer_shell_v1", {WLROOTS, "Layer shell", "wlr-layer-shell-unstable-v1"}}, - {"zwlr_output_manager_v1", {WLROOTS, "Output management", "wlr-output-management-unstable-v1"}}, - {"zwlr_output_power_manager_v1", {WLROOTS, "Output power management", "wlr-output-power-management-unstable-v1"}}, - {"zwlr_screencopy_manager_v1", {WLROOTS, "Screencopy", "wlr-screencopy-unstable-v1"}}, - {"zwlr_virtual_pointer_v1", {WLROOTS, "Virtual pointer", "wlr-virtual-pointer-unstable-v1"}}, - - {"org_kde_kwin_appmenu_manager", {KDE, "AppMenu", "appmenu"}}, - {"org_kde_kwin_blur_manager", {KDE, "Blur", "blur"}}, - {"org_kde_kwin_contrast_manager", {KDE, "Contrast", "contrast"}}, - {"org_kde_kwin_dpms_manager", {KDE, "DPMS", "dpms"}}, - {"org_kde_kwin_fake_input", {KDE, "Fake input", "fake-input"}}, - {"org_kde_kwin_idle", {KDE, "Idle", "idle"}}, - {"org_kde_kwin_keystate", {KDE, "Key state", "keystate"}}, - {"kde_lockscreen_overlay_v1", {KDE, "Lockscreen overlay", "kde-lockscreen-overlay-v1"}}, - {"org_kde_kwin_outputmanagement", {KDE, "Output management", "output-management"}}, - {"kde_output_management_v2", {KDE, "Output management v2", "kde-output-management-v2"}}, - {"org_kde_kwin_outputdevice", {KDE, "Output device", "outputdevice"}}, - {"kde_output_device_v2", {KDE, "Output device v2", "kde-output-device-v2"}}, - {"kde_output_order_v1", {KDE, "Output order", "kde-output-order-v1"}}, - {"org_kde_plasma_shell", {KDE, "Plasma shell", "plasma-shell"}}, - {"org_kde_plasma_virtual_desktop_management", {KDE, "Plasma virtual desktop", "plasma-virtual-desktop"}}, - {"org_kde_plasma_window_management", {KDE, "Plasma window management", "plasma-window-management"}}, - {"kde_primary_output_v1", {KDE, "Primary output", "kde-primary-output-v1"}}, - {"zkde_screencast_unstable_v1", {KDE, "Screencast", "zkde-screencast-unstable-v1"}}, - {"org_kde_kwin_server_decoration_manager", {KDE, "Server decoration", "server-decoration"}}, - {"org_kde_kwin_server_decoration_palette_manager", {KDE, "Server decoration palette", "server-decoration-palette"}}, - {"org_kde_kwin_shadow_manager", {KDE, "Shadow", "shadow"}}, - {"org_kde_kwin_slide_manager", {KDE, "Slide", "slide"}}, - - {"ivi_application", {WESTON, "In-vehicle infotainment application", "ivi-application"}}, - {"ivi_hmi_controller", {WESTON, "In-vehicle infotainment HMI controller", "ivi-hmi-controller"}}, - {"text_cursor_position", {WESTON, "Text cursor position", "text-cursor-position"}}, - {"weston_content_protection", {WESTON, "Content protection", "weston-content-protection"}}, - {"weston_desktop_shell", {WESTON, "Desktop shell", "weston-desktop-shell"}}, - {"weston_direct_display_v1", {WESTON, "Direct display", "weston-direct-display"}}, - {"weston_capture_v1", {WESTON, "Output capture", "weston-output-capture"}}, - {"weston_screenshooter", {WESTON, "Screenshooter", "weston-screenshooter"}}, - {"weston_touch_calibration", {WESTON, "Touch calibration", "weston-touch-calibration"}}, + {"wp_presentation", {UPSTREAM, STABLE, "Presentation time", "presentation-time"}}, + {"wp_viewporter", {UPSTREAM, STABLE, "Viewporter", "viewporter"}}, + {"xdg_wm_base", {UPSTREAM, STABLE, "XDG shell", "xdg-shell"}}, + + {"xdg_activation_v1", {UPSTREAM, STAGING, "XDG activation", "xdg-activation-v1"}}, + {"wp_drm_lease_device_v1", {UPSTREAM, STAGING, "DRM lease", "drm-lease-v1"}}, + {"ext_session_lock_manager_v1", {UPSTREAM, STAGING, "Session lock", "ext-session-lock-v1"}}, + {"wp_single_pixel_buffer_manager_v1", {UPSTREAM, STAGING, "Single-pixel buffer", "single-pixel-buffer-v1"}}, + {"wp_content_type_manager_v1", {UPSTREAM, STAGING, "Content type hint", "content-type-v1"}}, + {"ext_idle_notifier_v1", {UPSTREAM, STAGING, "Idle notify", "ext-idle-notify-v1"}}, + {"wp_tearing_control_manager_v1", {UPSTREAM, STAGING, "Tearing control", "tearing-control-v1"}}, + {"xwayland_shell_v1", {UPSTREAM, STAGING, "Xwayland shell", "xwayland-shell-v1"}}, + {"wp_fractional_scale_manager_v1", {UPSTREAM, STAGING, "Fractional scale", "fractional-scale-v1"}}, + {"wp_cursor_shape_manager_v1", {UPSTREAM, STAGING, "Cursor shape", "cursor-shape-v1"}}, + {"ext_foreign_toplevel_list_v1", {UPSTREAM, STAGING, "Foreign toplevel list", "ext-foreign-toplevel-list-v1"}}, + {"wp_security_context_manager_v1", {UPSTREAM, STAGING, "Security context", "security-context-v1"}}, + + {"zwp_fullscreen_shell_v1", {UPSTREAM, UNSTABLE, "Fullscreen shell", "fullscreen-shell-unstable-v1"}}, + {"zwp_idle_inhibit_manager_v1", {UPSTREAM, UNSTABLE, "Idle inhibit", "idle-inhibit-unstable-v1"}}, + {"zwp_input_method_context_v1", {UPSTREAM, UNSTABLE, "Input method", "input-method-unstable-v1"}}, + {"zwp_input_timestamps_manager_v1", {UPSTREAM, UNSTABLE, "Input timestamps", "input-timestamps-unstable-v1"}}, + {"zwp_keyboard_shortcuts_inhibit_manager_v1", {UPSTREAM, UNSTABLE, "Keyboard shortcuts inhibit", "keyboard-shortcuts-inhibit-unstable-v1"}}, + {"zwp_linux_dmabuf_v1", {UPSTREAM, UNSTABLE, "Linux DMA-BUF", "linux-dmabuf-unstable-v1"}}, + {"zwp_linux_explicit_synchronization_v1", {UPSTREAM, UNSTABLE, "Linux explicit synchronization", "linux-explicit-synchronization-unstable-v1"}}, + {"zwp_pointer_constraints_v1", {UPSTREAM, UNSTABLE, "Pointer constraints", "pointer-constraints-unstable-v1"}}, + {"zwp_pointer_gestures_v1", {UPSTREAM, UNSTABLE, "Pointer gestures", "pointer-gestures-unstable-v1"}}, + {"zwp_primary_selection_device_manager_v1", {UPSTREAM, UNSTABLE, "Primary selection", "primary-selection-unstable-v1"}}, + {"zwp_relative_pointer_manager_v1", {UPSTREAM, UNSTABLE, "Relative pointer", "relative-pointer-unstable-v1"}}, + {"zwp_tablet_manager_v2", {UPSTREAM, UNSTABLE, "Tablet", "tablet-unstable-v2"}}, + {"zwp_text_input_v3", {UPSTREAM, UNSTABLE, "Text input", "text-input-unstable-v3"}}, + {"zxdg_decoration_manager_v1", {UPSTREAM, UNSTABLE, "XDG decoration", "xdg-decoration-unstable-v1"}}, + {"zxdg_exporter_v2", {UPSTREAM, UNSTABLE, "XDG foreign", "xdg-foreign-unstable-v2"}}, + {"zxdg_output_manager_v1", {UPSTREAM, UNSTABLE, "XDG output", "xdg-output-unstable-v1"}}, + {"zwp_xwayland_keyboard_grab_manager_v1", {UPSTREAM, UNSTABLE, "Xwayland keyboard grabbing", "xwayland-keyboard-grab-unstable-v1"}}, + + {"zwlr_data_control_manager_v1", {WLROOTS, NONE, "Data control", "wlr-data-control-unstable-v1"}}, + {"zwlr_export_dmabuf_manager_v1", {WLROOTS, NONE, "Export DMA-BUF", "wlr-export-dmabuf-unstable-v1"}}, + {"zwlr_foreign_toplevel_manager_v1", {WLROOTS, NONE, "Foreign toplevel management", "wlr-foreign-toplevel-management-unstable-v1"}}, + {"zwlr_gamma_control_manager_v1", {WLROOTS, NONE, "Gamma control", "wlr-gamma-control-unstable-v1"}}, + {"zwlr_input_inhibit_manager_v1", {WLROOTS, NONE, "Input inhibitor", "wlr-input-inhibitor-unstable-v1"}}, + {"zwlr_layer_shell_v1", {WLROOTS, NONE, "Layer shell", "wlr-layer-shell-unstable-v1"}}, + {"zwlr_output_manager_v1", {WLROOTS, NONE, "Output management", "wlr-output-management-unstable-v1"}}, + {"zwlr_output_power_manager_v1", {WLROOTS, NONE, "Output power management", "wlr-output-power-management-unstable-v1"}}, + {"zwlr_screencopy_manager_v1", {WLROOTS, NONE, "Screencopy", "wlr-screencopy-unstable-v1"}}, + {"zwlr_virtual_pointer_manager_v1", {WLROOTS, NONE, "Virtual pointer", "wlr-virtual-pointer-unstable-v1"}}, + + {"org_kde_kwin_appmenu_manager", {KDE, NONE, "AppMenu", "appmenu"}}, + {"org_kde_kwin_blur_manager", {KDE, NONE, "Blur", "blur"}}, + {"org_kde_kwin_contrast_manager", {KDE, NONE, "Contrast", "contrast"}}, + {"org_kde_kwin_dpms_manager", {KDE, NONE, "DPMS", "dpms"}}, + {"org_kde_kwin_fake_input", {KDE, NONE, "Fake input", "fake-input"}}, + {"org_kde_kwin_idle", {KDE, NONE, "Idle", "idle"}}, + {"org_kde_kwin_keystate", {KDE, NONE, "Key state", "keystate"}}, + {"kde_lockscreen_overlay_v1", {KDE, NONE, "Lockscreen overlay", "kde-lockscreen-overlay-v1"}}, + {"org_kde_kwin_outputmanagement", {KDE, NONE, "Output management", "output-management"}}, + {"kde_output_management_v2", {KDE, NONE, "Output management v2", "kde-output-management-v2"}}, + {"org_kde_kwin_outputdevice", {KDE, NONE, "Output device", "outputdevice"}}, + {"kde_output_device_v2", {KDE, NONE, "Output device v2", "kde-output-device-v2"}}, + {"kde_output_order_v1", {KDE, NONE, "Output order", "kde-output-order-v1"}}, + {"org_kde_plasma_shell", {KDE, NONE, "Plasma shell", "plasma-shell"}}, + {"org_kde_plasma_virtual_desktop_management", {KDE, NONE, "Plasma virtual desktop", "plasma-virtual-desktop"}}, + {"org_kde_plasma_window_management", {KDE, NONE, "Plasma window management", "plasma-window-management"}}, + {"kde_primary_output_v1", {KDE, NONE, "Primary output", "kde-primary-output-v1"}}, + {"zkde_screencast_unstable_v1", {KDE, NONE, "Screencast", "zkde-screencast-unstable-v1"}}, + {"org_kde_kwin_server_decoration_manager", {KDE, NONE, "Server decoration", "server-decoration"}}, + {"org_kde_kwin_server_decoration_palette_manager", {KDE, NONE, "Server decoration palette", "server-decoration-palette"}}, + {"org_kde_kwin_shadow_manager", {KDE, NONE, "Shadow", "shadow"}}, + {"org_kde_kwin_slide_manager", {KDE, NONE, "Slide", "slide"}}, + + {"ivi_application", {WESTON, NONE, "In-vehicle infotainment application", "ivi-application"}}, + {"ivi_hmi_controller", {WESTON, NONE, "In-vehicle infotainment HMI controller", "ivi-hmi-controller"}}, + {"text_cursor_position", {WESTON, NONE, "Text cursor position", "text-cursor-position"}}, + {"weston_content_protection", {WESTON, NONE, "Content protection", "weston-content-protection"}}, + {"weston_desktop_shell", {WESTON, NONE, "Desktop shell", "weston-desktop-shell"}}, + {"weston_direct_display_v1", {WESTON, NONE, "Direct display", "weston-direct-display"}}, + {"weston_capture_v1", {WESTON, NONE, "Output capture", "weston-output-capture"}}, + {"weston_screenshooter", {WESTON, NONE, "Screenshooter", "weston-screenshooter"}}, + {"weston_touch_calibration", {WESTON, NONE, "Touch calibration", "weston-touch-calibration"}}, + + {"zaura_shell", {CHROMEOS, NONE, "Aura shell", "surface-augmenter"}}, + {"zcr_color_manager_v1", {CHROMEOS, NONE, "Color management", "chrome-color-management"}}, + {"overlay_prioritizer", {CHROMEOS, NONE, "Overlay prioritizer", "overlay-prioritizer"}}, + {"surface_augmenter", {CHROMEOS, NONE, "Surface augmenter", "surface-augmenter"}}, }; // clang-format on +std::string source_to_string(ProtocolSource source); std::string status_to_string(ProtocolStatus status); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/src/window.cpp new/waycheck-v1.0.0/src/window.cpp --- old/waycheck-v0.2.0/src/window.cpp 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/src/window.cpp 2023-10-15 20:51:45.000000000 +0200 @@ -6,6 +6,8 @@ #include <QAbstractItemView> #include <QCheckBox> #include <QGuiApplication> +#include <QLabel> +#include <QShortcut> #include <fstream> #include <sys/socket.h> #include <unistd.h> @@ -58,21 +60,22 @@ } Window::Window(QNativeInterface::QWaylandApplication* waylandApp, QWidget* parent) - : QMainWindow(parent), upstreamModel(0, 4), wlrootsModel(0, 3), kdeModel(0, 3), westonModel(0, 3), - ui(std::make_unique<Ui::Window>()) { + : QMainWindow(parent), ui(std::make_unique<Ui::Window>()), searchBox(ui->toolbar), supportBox(ui->toolbar) { ui->setupUi(this); - initTable(upstreamModel, *ui->upstreamTable); - initTable(wlrootsModel, *ui->wlrootsTable, false); - initTable(kdeModel, *ui->kdeTable, false); - initTable(westonModel, *ui->westonTable, false); + initTable(UPSTREAM, *ui->upstreamTable); + initTable(WLROOTS, *ui->wlrootsTable); + initTable(KDE, *ui->kdeTable); + initTable(WESTON, *ui->westonTable); + initTable(CHROMEOS, *ui->chromeosTable); + initTable(UNKNOWN, *ui->unknownTable); for (auto protocol : known_protocols) { addProtocol(protocol.second); } ui->upstreamTable->sortByColumn(1, Qt::AscendingOrder); - for (auto* table : {ui->upstreamTable, ui->wlrootsTable, ui->kdeTable, ui->westonTable}) { + for (auto* table : {ui->upstreamTable, ui->wlrootsTable, ui->kdeTable, ui->westonTable, ui->chromeosTable}) { table->sortByColumn(0, Qt::AscendingOrder); } @@ -80,38 +83,75 @@ auto fd = wl_display_get_fd(display); auto pid = pid_from_fd(fd); auto pname = process_name_from_pid(pid); - ui->compositor->setText(QString::asprintf("Compositor: %s", pname.c_str())); auto listener = wl_registry_listener{.global = registry_global, .global_remove = registry_global_remove}; auto* registry = wl_display_get_registry(display); wl_registry_add_listener(registry, &listener, (void*) this); wl_display_roundtrip(display); + + // Toolbar components + QLabel* compositor = new QLabel(ui->toolbar); + compositor->setText(QString::asprintf("Compositor: %s", pname.c_str())); + compositor->setContentsMargins(4, 0, 0, 0); + QFont font = compositor->font(); + font.setPointSize(14); + compositor->setFont(font); + ui->toolbar->addWidget(compositor); + + QWidget* spacer = new QWidget(ui->toolbar); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + ui->toolbar->addWidget(spacer); + + searchBox.setPlaceholderText("Searchâ¦"); + searchBox.setContentsMargins(4, 0, 4, 0); + searchBox.setFixedWidth(16 * 14); + QShortcut* shortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F), &searchBox); + QObject::connect(shortcut, &QShortcut::activated, [this]() { searchBox.setFocus(); }); + + ui->toolbar->addWidget(&searchBox); + + supportBox.addItem("All protocols", Model::supportFilters::ALL); + supportBox.addItem("Supported protocols", Model::supportFilters::SUPPORTED); + supportBox.addItem("Unsupported protocols", Model::supportFilters::UNSUPPORTED); + ui->toolbar->addWidget(&supportBox); } Window::~Window() = default; void Window::newGlobal(const std::string interface, const uint32_t version) { const Protocol& protocol = known_protocols[interface]; - if (protocol.status == UNKNOWN) { - return; - } + if (protocol.source == UNKNOWN) { + auto versionItem = new QStandardItem(false); + versionItem->setCheckState(Qt::Checked); + versionItem->setText(QString::asprintf("%d", version)); + + auto items = QList<QStandardItem*>({new QStandardItem(QString::fromStdString(interface)), versionItem}); - auto* model = modelForStatus(protocol.status); - if (model == nullptr) { + for (auto item : std::as_const(items)) { + item->setData(true, Qt::UserRole); + auto font = item->font(); + font.setWeight(QFont::DemiBold); + item->setFont(font); + item->setData(true, Qt::UserRole); + } + + models[protocol.source]->sourceModel.appendRow(items); return; } - auto column = (model == &upstreamModel) ? 2 : 1; - auto matches = model->findItems(QString::fromStdString(protocol.name), Qt::MatchExactly, column); + auto& model = models[protocol.source]; + + auto column = (model->includeStatus) ? 2 : 1; + auto matches = model->sourceModel.findItems(QString::fromStdString(protocol.name), Qt::MatchExactly, column); for (auto match : matches) { - auto versionItem = model->item(match->row(), column + 1); + auto versionItem = model->sourceModel.item(match->row(), column + 1); versionItem->setCheckState(Qt::Checked); versionItem->setText(QString::asprintf("%d", version)); - for (auto i = 0; i < model->columnCount(); i++) { - auto item = model->item(match->row(), i); + for (auto i = 0; i < model->sourceModel.columnCount(); i++) { + auto item = model->sourceModel.item(match->row(), i); auto font = item->font(); font.setWeight(QFont::DemiBold); item->setFont(font); @@ -121,10 +161,7 @@ } void Window::addProtocol(const Protocol& protocol) { - auto* model = modelForStatus(protocol.status); - if (model == nullptr) { - return; - } + auto& model = models[protocol.source]; auto versionItem = new QStandardItem(false); versionItem->setCheckState(Qt::Unchecked); @@ -135,7 +172,7 @@ new QStandardItem(QString::fromStdString(protocol.name)), versionItem, }); - if (protocol.status <= UNSTABLE) { + if (protocol.source == UPSTREAM) { auto* statusItem = new QStandardItem(QString::fromStdString(status_to_string(protocol.status))); statusItem->setTextAlignment(Qt::AlignCenter); items.prepend(statusItem); @@ -145,48 +182,92 @@ item->setData(false, Qt::UserRole); } - model->appendRow(items); + model->sourceModel.appendRow(items); } -void Window::initTable(QStandardItemModel& model, QTableView& table, bool includeStatus) { - table.setModel(&model); +void Window::initTable(const ProtocolSource source, QTableView& table) { + models[source] = std::make_unique<Model>(source); + auto& model = models[source]; + + table.setModel(model->filteredModel); table.setSortingEnabled(true); + table.setEditTriggers(QAbstractItemView::NoEditTriggers); + table.setSelectionMode(QAbstractItemView::SingleSelection); + table.verticalHeader()->setVisible(false); + table.setItemDelegate(new TableItemDelegate); - if (includeStatus) { - model.setHorizontalHeaderLabels({"Status", "Name", "Identifier", "Implementation"}); - table.horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); - table.horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); + if (source == UPSTREAM) { + table.horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + table.horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + table.horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents); table.horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents); - table.horizontalHeader()->setSectionResizeMode(4, QHeaderView::ResizeToContents); - } else { - model.setHorizontalHeaderLabels({"Name", "Identifier", "Implementation"}); + } else if (source != UNKNOWN) { table.horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); table.horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); table.horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + } else { + table.horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + table.horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); } - table.sortByColumn(0, Qt::AscendingOrder); + table.sortByColumn(0, Qt::AscendingOrder); table.setEditTriggers(QAbstractItemView::NoEditTriggers); table.setSelectionMode(QAbstractItemView::SingleSelection); table.verticalHeader()->setVisible(false); table.setItemDelegate(new TableItemDelegate); -} + table.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); -QStandardItemModel* Window::modelForStatus(const ProtocolStatus status) { - switch (status) { - case STABLE: - case STAGING: - case UNSTABLE: - return &upstreamModel; - case WLROOTS: - return &wlrootsModel; - case KDE: - return &kdeModel; - case WESTON: - return &westonModel; - default: - return nullptr; - } + // Connect filters to UI + QObject::connect(&searchBox, &QLineEdit::textChanged, [&model](const QString& text) { model->setSearchFilter(text); }); + + QObject::connect(&supportBox, &QComboBox::currentIndexChanged, [&model, this](const int& index) { + model->setSupportFilter(static_cast<Model::supportFilters>(supportBox.itemData(index).toInt())); + }); + + // Resize table columns when model is updated + auto resizeColumns = [&table]() { + table.setVisible(false); + table.resizeColumnsToContents(); + table.setVisible(true); + }; + + QObject::connect(model->filteredModel, &QSortFilterProxyModel::rowsInserted, resizeColumns); + QObject::connect(model->filteredModel, &QSortFilterProxyModel::rowsRemoved, resizeColumns); + + // Set tab title + auto titleTab = [this, &model, &table](bool filtering) { + int index = ui->toolBox->indexOf(&table); + int count = model->filteredModel->rowCount(); + + bool tabVisible = ui->toolBox->isTabVisible(index); + bool tabShouldBeVisible = true; + + if (count == 0) { + tabShouldBeVisible = false; + } else if (!filtering) { + ui->toolBox->setTabText(index, model->name); + } else { + ui->toolBox->setTabText(index, QStringLiteral("%1 (%2)").arg(model->name).arg(count)); + } + + if (tabVisible != tabShouldBeVisible) { + ui->toolBox->setTabVisible(index, tabShouldBeVisible); + } + + bool tabsVisible = false; + for (int i = 0; i < ui->toolBox->count(); i++) { + if (ui->toolBox->isTabVisible(i) == true) { + tabsVisible = true; + break; + } + } + + ui->toolBox->setVisible(tabsVisible); + ui->filterMessage->setVisible(!tabsVisible); + }; + + titleTab(false); + QObject::connect(model.get(), &Model::filterChanged, titleTab); } TableItemDelegate::TableItemDelegate(QObject* parent) : QStyledItemDelegate(parent) {} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/src/window.hpp new/waycheck-v1.0.0/src/window.hpp --- old/waycheck-v0.2.0/src/window.hpp 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/src/window.hpp 2023-10-15 20:51:45.000000000 +0200 @@ -1,14 +1,17 @@ #ifndef WINDOW_HPP #define WINDOW_HPP +#include "model.hpp" #include "protocols.hpp" +#include <QComboBox> #include <QGuiApplication> +#include <QLineEdit> #include <QMainWindow> -#include <QStandardItemModel> #include <QStyledItemDelegate> #include <QTableView> +#include <functional> #include <memory> QT_BEGIN_NAMESPACE @@ -19,10 +22,7 @@ class Window : public QMainWindow { Q_OBJECT - QStandardItemModel upstreamModel; - QStandardItemModel wlrootsModel; - QStandardItemModel kdeModel; - QStandardItemModel westonModel; + std::map<ProtocolSource, std::unique_ptr<Model>> models; public: Window(QNativeInterface::QWaylandApplication* waylandApp, QWidget* parent = nullptr); @@ -33,12 +33,14 @@ void addProtocol(const Protocol& protocol); private: - void initTable(QStandardItemModel& model, QTableView& table, bool includeStatus = true); + void initTable(const ProtocolSource source, QTableView& table); - QStandardItemModel* modelForStatus(const ProtocolStatus status); + Model* modelForStatus(const ProtocolStatus status); private: std::unique_ptr<Ui::Window> const ui; + QLineEdit searchBox; + QComboBox supportBox; }; class TableItemDelegate : public QStyledItemDelegate { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waycheck-v0.2.0/src/window.ui new/waycheck-v1.0.0/src/window.ui --- old/waycheck-v0.2.0/src/window.ui 2023-09-28 17:40:53.000000000 +0200 +++ new/waycheck-v1.0.0/src/window.ui 2023-10-15 20:51:45.000000000 +0200 @@ -13,6 +13,26 @@ <property name="windowTitle"> <string>Waycheck</string> </property> + <widget class="QToolBar" name="toolbar"> + <property name="windowTitle"> + <string>Toolbar</string> + </property> + <property name="movable"> + <bool>false</bool> + </property> + <property name="allowedAreas"> + <set>Qt::BottomToolBarArea|Qt::TopToolBarArea</set> + </property> + <property name="floatable"> + <bool>false</bool> + </property> + <attribute name="toolBarArea"> + <enum>TopToolBarArea</enum> + </attribute> + <attribute name="toolBarBreak"> + <bool>false</bool> + </attribute> + </widget> <widget class="QWidget" name="centralwidget"> <layout class="QVBoxLayout" name="verticalLayout"> <property name="spacing"> @@ -31,97 +51,34 @@ <number>8</number> </property> <item> - <widget class="QLabel" name="compositor"> + <widget class="QTabWidget" name="toolBox"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QTableView" name="upstreamTable"/> + <widget class="QTableView" name="wlrootsTable"/> + <widget class="QTableView" name="kdeTable"/> + <widget class="QTableView" name="westonTable"/> + <widget class="QTableView" name="chromeosTable"/> + <widget class="QTableView" name="unknownTable"/> + </widget> + </item> + <item> + <widget class="QLabel" name="filterMessage"> + <property name="text"> + <string>No protocols match the current search</string> + </property> <property name="font"> <font> - <pointsize>16</pointsize> + <pointsize>14</pointsize> </font> </property> - <property name="bottomMargin"> - <number>4</number> - </property> - <property name="text"> - <string>TextLabel</string> - </property> <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> - </property> - </widget> - </item> - <item> - <widget class="QTabWidget" name="toolBox"> - <property name="currentIndex"> - <number>0</number> + <set>Qt::AlignCenter</set> </property> - <widget class="QWidget" name="upstreamTool"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - </rect> - </property> - <attribute name="title"> - <string>Upstream</string> - </attribute> - <layout class="QVBoxLayout" name="verticalLayout_1"> - <item> -<widget class="QTableView" name="upstreamTable"/> - </item> - </layout> - </widget> - <widget class="QWidget" name="wlrootsTool"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - </rect> - </property> - <attribute name="title"> - <string>Wlroots</string> - </attribute> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item row="0" column="0"> - <widget class="QTableView" name="wlrootsTable"/> - </item> - </layout> - </widget> - <widget class="QWidget" name="kdeTool"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - </rect> - </property> - <attribute name="title"> - <string>KDE</string> - </attribute> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <widget class="QTableView" name="kdeTable"/> - </item> - </layout> - </widget> - <widget class="QWidget" name="westonTool"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - </rect> - </property> - <attribute name="title"> - <string>Weston</string> - </attribute> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <item> - <widget class="QTableView" name="westonTable"/> - </item> - </layout> - </widget> </widget> </item> </layout> </widget> </widget> - <resources/> - <connections/> </ui>