Compare commits

..

131 Commits
0.94.1 ... main

Author SHA1 Message Date
Juan Pablo Ugarte
53062b7d3c
Rolling 0.97.3 2025-06-22 16:41:07 -04:00
Juan Pablo Ugarte
9aaace82f7
Update Casilda dependency to 0.9.0
Remove merengue GSK_RENDERER var to enable GL client rendering.
2025-06-22 16:29:57 -04:00
Juan Pablo Ugarte
bbb2ce2bf9
cmb_init_dev add missing repository directory 2025-06-22 16:29:12 -04:00
Juan Pablo Ugarte
dd90c3fc2b
Bump to 0.97.2 2025-05-24 16:47:08 -04:00
Juan Pablo Ugarte
8742945f3f
CmbObjectEditor: allways pass info to data editors 2025-05-24 16:46:00 -04:00
Juan Pablo Ugarte
06c62349a7
CmbObjectDataEditor: rever changes made to fix data removal. 2025-05-24 16:45:31 -04:00
Juan Pablo Ugarte
bc6163b1ac
CmbObject: wrap remove_data() history commands 2025-05-24 16:44:44 -04:00
Juan Pablo Ugarte
ac74e23fde
CmbObjectData: group remove_data() history commands 2025-05-24 16:43:41 -04:00
Juan Pablo Ugarte
2e29c016f7
CmbNotification: log request error as info instead of warning 2025-05-24 16:43:01 -04:00
Juan Pablo Ugarte
b572a7eae3
Add blueprint support
- CmbProject: add blueprint load/save support
 - CmbWindow: show blueprint compiler errors on save
 - CmbUIEditor: add format dropdown
 - CmbView: show blueprint source code instead of XML
 - Add blueprint tests

Closes issue #80 "Support for blueprint file format"
2025-05-23 08:48:02 -04:00
Juan Pablo Ugarte
f96d19ab64
CmbDB: add private flags to control boolean and enum output 2025-05-21 19:08:34 -04:00
Juan Pablo Ugarte
680f9d3243
CmbFileButton: add mime_types, accept_label and unnamed_filename properties 2025-05-21 19:05:59 -04:00
Juan Pablo Ugarte
6fa7f22c9c
CmbTypeInfo: add use_nick param to enum_get_value_as_string() 2025-05-21 19:03:47 -04:00
Juan Pablo Ugarte
6ac16ca8c0
tests/test_import_export.py: Use parametrize 2025-05-21 19:01:53 -04:00
Juan Pablo Ugarte
e2dbb15a18
CmbSourceView: fix lang getter 2025-05-21 19:01:01 -04:00
Juan Pablo Ugarte
c57891a3c8
CmbUI: update display-name on filename or template-id change 2025-05-21 19:01:01 -04:00
Juan Pablo Ugarte
ccdf5d6ef0
CmbVersionNotificationView: do not show read more button if there is no link 2025-05-15 18:28:40 -04:00
Juan Pablo Ugarte
bb39873cd5
Rolling 0.97.1 2025-05-14 17:22:30 -04:00
Juan Pablo Ugarte
9529bfaf76
CmbObjectDataEditor: fix object data remove. 2025-05-14 17:17:53 -04:00
Juan Pablo Ugarte
13db1c20c2
CmbProject: fix GResource list model update 2025-05-14 11:40:44 -04:00
Juan Pablo Ugarte
963cee5eec
CmbResource: misc improvements
- Notify display-name when underlying props changes
 - Add private functions to update parent data needed to keep
   list model in sync
2025-05-14 11:40:44 -04:00
Juan Pablo Ugarte
08a73f9c28
CmbResourceEditor: use open for choosing a file 2025-05-14 11:40:44 -04:00
Juan Pablo Ugarte
02935555bf
CmbDB: add update_gresource_children_position() 2025-05-14 11:40:44 -04:00
Juan Pablo Ugarte
befb056d2c
CmbFileButton: add use_open property 2025-05-14 11:40:44 -04:00
Juan Pablo Ugarte
9cd6e05d5f
run-dev.py: Misc cleanups
- Make tests work without any special env set
 - Remove .local.env bash env
 - Simplify coverage script
2025-05-14 11:40:44 -04:00
Juan Pablo Ugarte
57a3f3832a
CmbNotification: generate UUID locally 2025-05-14 11:40:44 -04:00
Juan Pablo Ugarte
8c80fc5d3d
Bump to 0.97.0 - Development version 2025-04-21 07:25:10 -04:00
Juan Pablo Ugarte
7baf191c0b
Rolling 0.96.0 2025-04-20 10:09:40 -04:00
Juan Pablo Ugarte
8d170bab39
Update catalogs to SDK 48 2025-04-20 09:51:56 -04:00
Juan Pablo Ugarte
b592367640
cmb_catalog_gen: fix private library loading 2025-04-20 09:51:56 -04:00
Juan Pablo Ugarte
d41c08b68a
MrgApplication: fix css provider on load 2025-04-13 10:03:54 -04:00
Juan Pablo Ugarte
7de36972dd
data/cambalache-project.dtd: update format 2025-04-13 10:03:54 -04:00
Juan Pablo Ugarte
6b1ae2ad75
CmbProject: project file misc cleanups
- Add Cambalache version comment
 - Do not output cambalache version for unsaved ui files
 - Only save files when hash changes
2025-04-13 10:03:54 -04:00
Juan Pablo Ugarte
296d7767f9
Rolling 0.95.1
Test version for notifications
2025-04-03 20:10:21 -04:00
Juan Pablo Ugarte
e6e55da821
CmbNotificationListView: hide list view if model is empty 2025-04-03 20:10:08 -04:00
Juan Pablo Ugarte
530c211262
CmbNotification: disable notifications when using memory settings backend 2025-04-03 18:53:21 -04:00
Juan Pablo Ugarte
175d1d1b61
Port to SDK 48 and misc build cleanups
- Install private typelibs together with private libraries
 - Make Gtk3, handy and webkit optional
2025-03-27 20:53:20 -04:00
Juan Pablo Ugarte
1401c0c480
CmbNotifiaction: use keyfile to store settings if defaults to memory. 2025-03-11 11:21:12 -03:00
Juan Pablo Ugarte
acadc33f02
Tests: ensure tests dont get notification from backend 2025-03-09 12:46:38 -03:00
Juan Pablo Ugarte
9d8794cfb0
CmbNotification: add GSettings backend to user agent 2025-03-09 12:42:37 -03:00
Juan Pablo Ugarte
011c6dc082
Gtk Catalog: mark GtkComboBoxText item as translatable
Fix issue #273 "GtkComboBoxText items gets their translatable property removed"
2025-02-23 10:55:02 -03:00
Juan Pablo Ugarte
258f04825b
CmbNotification: minor cleanups
Add unit tests
2025-02-21 16:12:27 -03:00
Juan Pablo Ugarte
61893998dd
CmbWindow: add notification system 2025-02-16 17:10:50 -03:00
Juan Pablo Ugarte
7baa2cfbbe
CmbNotificationListView: add notification list view 2025-02-15 17:42:18 -03:00
Juan Pablo Ugarte
60f3e49672
CmbPollNotificationView: add poll notification view 2025-02-15 17:41:48 -03:00
Juan Pablo Ugarte
7213b0351f
CmbVersionNotificationView: add view for version notifications 2025-02-15 17:41:15 -03:00
Juan Pablo Ugarte
0709c8487b
CmbMessageNotificationView: add view for message notifications 2025-02-15 17:40:48 -03:00
Juan Pablo Ugarte
b2d95c576d
Utils: add utcnow() helper 2025-02-15 17:40:01 -03:00
Juan Pablo Ugarte
211d13b14a
CmbNotificationCenter: add main entry point to get app notifications 2025-02-15 17:38:51 -03:00
Juan Pablo Ugarte
5e16c964ee
MrgGtkLabel: handle use-markup property 2025-02-10 21:44:41 -03:00
Juan Pablo Ugarte
bda30ef76e
CmbView: use theme bg color on theme change
Should fix issue #272 "Background of compositor does not change colors, when adwaita colors are changed"
2025-02-06 19:19:03 -03:00
Juan Pablo Ugarte
8858257f6d
CmbView: fix merengue write command
- Loop until all data is written

Fix issue #269 "Failed to display some element of a validated ui file"
2025-01-29 21:41:33 -05:00
Juan Pablo Ugarte
14d0b3dd39
MrgApplication: add payload size to debug messages 2025-01-29 21:36:49 -05:00
Juan Pablo Ugarte
05bdb5d98e
CmbDB: strip object properties values 2025-01-29 21:35:43 -05:00
Juan Pablo Ugarte
af6258fe8a
MrgGtkMenu: improve window handling
Do not create custom window if menu is already attached to a widget.
2025-01-21 18:46:58 -05:00
Juan Pablo Ugarte
5de6214da2
MrgSelection: improve selection handling
- Select on button press instead of release
 - Do not consume initial events for GtkWindow and GtkHeaderBar
   This allows to move the window even if its not selected

Should close issue #267 "Make drag'n'drop of top-level more intuitive"
2025-01-21 18:39:51 -05:00
Juan Pablo Ugarte
4c8cbda88d
MrgApplication: removed unused preselected_widget member 2025-01-21 18:39:51 -05:00
Juan Pablo Ugarte
04ec6758f0
Catalogs: remove deprecated is-position flag 2025-01-17 19:17:20 -05:00
Juan Pablo Ugarte
ecf21c544d
CmbDB: cleanup is_position logic
- Do not try to unify position layout properties with object position.
 - Add special case for Gtk3 to generate placeholders using layout position property.

Fix issue #265 "GtkButtonBox shows too many buttons"
2025-01-17 18:57:04 -05:00
Juan Pablo Ugarte
a9c05ef202
CmbObject: remove position_layout_property logic 2025-01-17 18:56:35 -05:00
Juan Pablo Ugarte
df1dd90146
CmbLayoutProperty: remove is_position logic 2025-01-17 18:54:28 -05:00
Juan Pablo Ugarte
477c45a32c
cmb-catalog-gen: remove is-position logic 2025-01-17 18:52:56 -05:00
Juan Pablo Ugarte
99d58df208
Data Model: deprecate object is_model column 2025-01-17 18:52:23 -05:00
Juan Pablo Ugarte
5f69f152a1
CmbListError: add n_items property 2025-01-17 18:51:57 -05:00
Juan Pablo Ugarte
45f6cf920b
CmbProperty: ensure internal child tied to a property is created or removed.
Close issue #266 "Error "Unknown internal child: entry (6)" with particular GTK 3 UI file"
2025-01-16 18:05:41 -05:00
Juan Pablo Ugarte
425aed1872
CmbProject: fix api to allow creating internal objects 2025-01-16 18:05:06 -05:00
Juan Pablo Ugarte
0d93f151bf
CmbDB: Do not automatically create internal child that depend on a property 2025-01-16 18:04:40 -05:00
Juan Pablo Ugarte
a2945f2de2
CmbTypeInfo: backfill property info internal child property. 2025-01-16 18:03:28 -05:00
Juan Pablo Ugarte
34bdc5e530
CmbPropertyInfo: add internal_child property 2025-01-16 18:03:09 -05:00
Juan Pablo Ugarte
dcc4b0a199
Gtk Catalog: set GtkComboBox entry internal child creation property id.
GtkComboBox only creates entry internal child is has-entry is true
2025-01-16 18:01:15 -05:00
Juan Pablo Ugarte
3f23b4a9e7
Data Model: add creation_property_id to type_internal_child
This data is to known if there is a property that handles the
creation of the internal child.
2025-01-16 18:00:21 -05:00
Juan Pablo Ugarte
4db249bf28
CmbProject: ignore negative positions on undo/redo
Fix issue #264 "Error undoing removal of parent GtkGrid"
2025-01-15 19:00:55 -05:00
Juan Pablo Ugarte
8b02d13e01
run-dev.sh: Bind translation domain
This fixes running dev script in different languages.
2025-01-15 19:00:55 -05:00
Juan Pablo Ugarte
31b93cef82 Merge branch 'finnish' into 'main'
Finnish translation

See merge request jpu/cambalache!64
2025-01-15 14:30:01 +00:00
Erwinjitsu
c7885f5ab6 Finnish translation 2025-01-15 14:30:01 +00:00
Juan Pablo Ugarte
9a0db6c2f3
CmbProperty: fix property reset
Fix issue #263 "Translatable setting resets when label field is empty"
2025-01-10 18:16:47 -05:00
Juan Pablo Ugarte
cb1aed17b8
CmbListStore: remove unused file 2025-01-09 13:58:27 -05:00
Juan Pablo Ugarte
09d118dbc0
CmbDB: fix comment serialization 2025-01-06 17:46:14 -05:00
Juan Pablo Ugarte
829dc50bae
CmbWindow: make project name required when creating a new one 2025-01-06 17:46:14 -05:00
Juan Pablo Ugarte
94cce882aa
CmbDB: set swapped on signal import 2025-01-05 10:39:05 -05:00
Juan Pablo Ugarte
f0c1543982
CmbWindow: add back open dialog filter 2025-01-05 10:07:02 -05:00
Juan Pablo Ugarte
fb930b36da
cmb-catalog-gen: fix private library path 2025-01-05 14:51:20 -05:00
Juan Pablo Ugarte
48c004a1e6
Add private lib directory to GI repository path
Close issue #259 "Install private shared libraries in sub directories of the main library path"
2025-01-05 14:36:17 -05:00
Benson Muite
f3662b8095
Install private shared libraries in subdirectories 2025-01-05 10:13:19 -05:00
Juan Pablo Ugarte
986e3705b4
CmbApplication: fix open and import actionn parameters
Set empty string target_tk to None before importing file.

Close issue #255 "Unable to open files via the UI in a KDE Plasma session"
2025-01-04 15:17:46 -05:00
Juan Pablo Ugarte
e30b93506b
CmbDB: fix signal swap export when object is set.
Fix issue #260 "Wrong default for Swap setting in signals"
2025-01-03 19:17:16 -05:00
Juan Pablo Ugarte
907c4938a0
CmbWindow: ask user for gtk version on import
Ask user when importing a file with an undetermined target toolkit.

Close issue #255 "Unable to open files via the UI in a KDE Plasma session"
2025-01-03 17:28:23 -05:00
Juan Pablo Ugarte
874a506f47
CmbDB: add support for internal children
- Automatically create internal children when adding a new object
 - Do not export internal children when empty

Close issue #54 "Add support for internal children"
2024-12-28 15:27:32 -05:00
Juan Pablo Ugarte
2623583f3a
CmbWindow: show exception messages on delete action 2024-12-28 15:21:04 -05:00
Juan Pablo Ugarte
1f709586fb
CmbProject: refuse to delete an internal child 2024-12-28 15:21:04 -05:00
Juan Pablo Ugarte
6e1935d9fb
CmbObject: show internal name in display_name 2024-12-28 15:21:04 -05:00
Juan Pablo Ugarte
bc74332ecf
CmbTypeInfo: add internal_children member 2024-12-28 15:21:04 -05:00
Juan Pablo Ugarte
7aaabca392
CmbSourceView: make text property return None on empty string 2024-12-28 15:21:03 -05:00
Juan Pablo Ugarte
53854f6615
Data Model: add internal children support
- Add type_internal_child table
 - Extend xml catalog to define internal children
 - Infer internal child type from instance
 - Add internal children for varous types
2024-12-28 15:21:03 -05:00
Juan Pablo Ugarte
edee67cc72
Move coverage script to root 2024-12-22 12:34:46 -05:00
Juan Pablo Ugarte
937211565c
Update test images reference 2024-12-22 12:25:50 -05:00
Juan Pablo Ugarte
c6c278444f
tests/test_merengue_xml.py: fix project import_file() return values 2024-12-22 12:25:00 -05:00
Juan Pablo Ugarte
56b8af7f97
CmbProject: populate objects for old project 2024-12-22 12:24:23 -05:00
Juan Pablo Ugarte
526b880187
CmbDB: add warning if type info is not found 2024-12-22 12:23:49 -05:00
Juan Pablo Ugarte
e8cf3eb578
CmbShortcuts: add general section
Add missing shortcuts
2024-12-22 11:57:57 -05:00
Juan Pablo Ugarte
0e4d55eaa2
CmbObjectDataEditor: make grid not expand 2024-12-22 11:57:29 -05:00
Juan Pablo Ugarte
627c7a4fa9
Adw Catalog: disable AdwWindow titlebar and child properties 2024-12-22 11:26:01 -05:00
Juan Pablo Ugarte
7f6cd3c005
CmbFlagsEntry: add separator property 2024-12-22 11:19:07 -05:00
Juan Pablo Ugarte
ffdeb0c6c0
Metainfo: add unreleased notes 2024-12-15 16:52:15 -05:00
Juan Pablo Ugarte
d3e52c3b3b
CmbWindow: Unify ui, css and gresource import in one action. 2024-12-15 16:52:15 -05:00
Juan Pablo Ugarte
bfe13f87d4
CmbListView: improve CSS
- Use navitagion-sidebar CSS class as base
 - Add drop target CSS using outline and top/bottom border
2024-12-15 16:52:15 -05:00
Juan Pablo Ugarte
e4a6ae3e36
CmbProject: update path hierarchy on filename change 2024-12-15 16:52:15 -05:00
Juan Pablo Ugarte
e242742a29
CmbListView: port CmbColumnView to GtkListView
Show files in their directory structure
2024-12-14 10:24:44 -05:00
Juan Pablo Ugarte
458e93abce
CmbFileButton: add new control to edit filenames 2024-12-13 21:19:09 -05:00
Juan Pablo Ugarte
1c0d1a686a
Port Cambalache project to new version 2024-12-13 16:14:50 -05:00
Juan Pablo Ugarte
0ce1286acc
CmbProject: load UI in topological order
Add template-class and template-dependency arguments to ui element.
2024-12-13 16:14:50 -05:00
Juan Pablo Ugarte
f6ffc64988
CmbWindow: add GResource support
Close issue #145 "Consider Cambalache to manage resource description file for building the resource bundle"
2024-12-13 16:14:50 -05:00
Juan Pablo Ugarte
5ca4aa2377
CmbProject: Add support for GResource
Moved load and save functionality from CmbDB.
Remove unused export functions
2024-12-13 16:14:48 -05:00
Juan Pablo Ugarte
2468a22178
CmbDB: Add support for GResource
Remove project load and save functionality.
2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
3b6b8023af
CmbGResourceEditor: add new basic editor 2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
afd241b7ea
CmbApplication: deprecate export-all option 2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
23c6576514
CmbColumnView: add support for CmbGResource 2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
4c219cd58a
CmbShortcuts: remove export shortcut 2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
39b47123dd
CmbTutorial: remove mention to export 2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
f941661e49
CmbUI, CmbUIEditor: remove export and remove buttons 2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
572bf8f1ac
CmbCSSEditor: remove remove button 2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
097ccce2f7
CmbGResource: add new object wrapper for new gresource table
Add new table to store GResource data in DB
2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
530ed75c79
cambalache/utils.py: add xml utilities and FileHash class 2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
be19adfba0
CmbDB: change project file format.
Store reference to XML files instead of exporting them.

Originally the import/export process was separated because Cambalache
did not supported all GtkBuilder features and I was trying to avoid
users assume that they could manually edit the XML files and everything
would keep working.

Now thanks to XML fragments fallback support, Cambalache will honor most
of the GtkBuilder features including custom biuildable iface tags

Main benefits of the new format are:
 - No need to export files.
 - Saving the project will update XML UI files
 - Merge friendly
2024-12-13 16:13:37 -05:00
Juan Pablo Ugarte
cf7a91ceb4
Data Model: improve history table
- Replace column_name TEXT with columns JSON

This allows to keep track of updates of multiple columns at the same
time, which is needed for unique indexes with more than one column.

This fixes error undoing a parent change like in D&D
2024-12-08 16:11:07 -05:00
Juan Pablo Ugarte
c88712db76
Gtk Catalog: add action child type to GtkDialog 2024-12-10 21:12:57 -05:00
Juan Pablo Ugarte
9ea01bdec7 Merge branch 'use-adw-about-dialog' into 'main'
Use AdwAboutDialog

See merge request jpu/cambalache!62
2024-12-02 02:49:17 +00:00
lo-vely
6b323e4475 Use AdwAboutDialog 2024-11-29 12:53:01 +02:00
Juan Pablo Ugarte
ee660f396e
CmbProject: cleanup UI and CSS display name on undo/redo messages 2024-11-26 18:42:58 -05:00
Juan Pablo Ugarte
92cc81894a
CmbUI, CmbCSS: add get_display_name() class method 2024-11-26 18:38:48 -05:00
Juan Pablo Ugarte
db70b1c2b8
Update test image ref 2024-11-26 18:04:06 -05:00
140 changed files with 12461 additions and 6108 deletions

View File

@ -1,12 +0,0 @@
#!/usr/bin/bash
SCRIPT=$(readlink -f $0)
DIRNAME=$(dirname $SCRIPT)
ARCH_TRIPLET=$(cc -dumpmachine)
export LD_LIBRARY_PATH=$DIRNAME/.local/lib/$ARCH_TRIPLET:$LD_LIBRARY_PATH
export GI_TYPELIB_PATH=$DIRNAME/.local/lib/$ARCH_TRIPLET/girepository-1.0:$GI_TYPELIB_PATH
export PKG_CONFIG_PATH=$DIRNAME/.local/lib/$ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
export GSETTINGS_SCHEMA_DIR=$DIRNAME/.local/share/glib-2.0/schemas:$GSETTINGS_SCHEMA_DIR
export XDG_DATA_DIRS=$DIRNAME/.local/share:$XDG_DATA_DIRS
export PYTHONPATH=$DIRNAME/.local/lib/python3/dist-packages:$PYTHONPATH
export PATH=$DIRNAME/.local/bin:$PATH

View File

@ -8,17 +8,42 @@ packaging changes like new dependencies or build system changes.
Cambalache used even/odd minor numbers to differentiate between stable and
development releases.
## 0.94.1
2024-11-26 - First bugfix release in the series!
## 0.96.0
- Fix issue with undoing a parent change.
- Fix workspace initialization error
- Fix warning on undo/redo message generation
- Add GResource support
- Add internal children support
- New project format
- Save directly to .ui files
- Show directory structure in navigation
- Unified import dialog for all file types
- Add Finnish translation. Erwinjitsu
- Use AdwAboutDialog lo-vely
- Add action child type to GtkDialog
### Packaging changes
- pygobject-3.0 dependency bumped to 3.52 which depends on the new gi repository from GLib
- libcambalacheprivate-[3|4] and its typelib are now installed under libdir/cambalache
- libcmbcatalogutils-[3|4] and its typelib are now installed under libdir/cmb_catalog_gen
- Gtk 3, Handy, webkit2gtk and webkitgtk are now optional dependencies
### Issues
- #253 "Error updating UI 1: gtk-builder-error-quark: .:8:1 Invalid object type 'AdwApplicationWindow' (6)"
- #145 "Consider Cambalache to manage resource description file for building the resource bundle"
- #54 "Add support for internal children"
- #255 "Unable to open files via the UI in a KDE Plasma session"
- #260 "Wrong default for Swap setting in signals"
- #259 "Install private shared libraries in sub directories of the main library path"
- #263 "Translatable setting resets when label field is empty"
- #264 "Error undoing removal of parent GtkGrid"
- #266 "Error "Unknown internal child: entry (6)" with particular GTK 3 UI file"
- #265 "GtkButtonBox shows too many buttons"
- #267 "Make drag'n'drop of top-level more intuitive"
- #269 "Failed to display some element of a validated ui file"
- #272 "Background of compositor does not change colors, when adwaita colors are changed"
- #273 "GtkComboBoxText items gets their translatable property removed"
## 0.94.0

View File

@ -1,7 +1,7 @@
repo: ar.xjuan.Cambalache.json .git/objects
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install --noninteractive --user flathub org.gnome.Sdk//47
flatpak install --noninteractive --user flathub org.gnome.Platform//47
flatpak install --noninteractive --user flathub org.gnome.Sdk//48
flatpak install --noninteractive --user flathub org.gnome.Platform//48
flatpak-builder --force-clean --repo=repo build ar.xjuan.Cambalache.json
cambalache.flatpak: repo

View File

@ -152,7 +152,7 @@ cambalache under .local directoy and set up all environment variables needed to
run the app from the source directory. (Follow manual installation to ensure
you have everything needed)
`./run-dev.sh`
`./run-dev.py`
This is meant for Cambalache development only.

View File

@ -1,7 +1,7 @@
{
"app-id" : "ar.xjuan.Cambalache",
"runtime" : "org.gnome.Platform",
"runtime-version" : "47",
"runtime-version" : "48",
"sdk" : "org.gnome.Sdk",
"separate-locales" : false,
"command" : "cambalache",
@ -34,8 +34,8 @@
"sources" : [
{
"type" : "file",
"url" : "https://files.pythonhosted.org/packages/63/f7/ffbb6d2eb67b80a45b8a0834baa5557a14a5ffce0979439e7cd7f0c4055b/lxml-5.2.2.tar.gz",
"sha256" : "bb2dc4898180bea79863d5487e5f9c7c34297414bad54bcd0f0852aee9cfdb87"
"url" : "https://files.pythonhosted.org/packages/80/61/d3dc048cd6c7be6fe45b80cedcbdd4326ba4d550375f266d9f4246d0f4bc/lxml-5.3.2.tar.gz",
"sha256" : "773947d0ed809ddad824b7b14467e1a481b8976e87278ac4a730c2f7c7fcddc1"
}
]
},
@ -83,8 +83,8 @@
{
"type" : "git",
"url" : "https://gitlab.gnome.org/jpu/casilda.git",
"tag" : "0.2.0",
"commit" : "99a0173f21345b85713198c1fa1fbb388d00182f"
"tag" : "0.9.0",
"commit" : "4f7b1be321cf76832b12bda11fd91897257377e2"
}
]
},

View File

@ -30,10 +30,12 @@ import builtins
from . import config
gi.require_version("GIRepository", "3.0")
gi.require_version("Gdk", "4.0")
gi.require_version("Gtk", "4.0")
gi.require_version("GtkSource", "5")
gi.require_version("WebKit", "6.0")
gi.require_version('Adw', '1')
# Ensure _() builtin
if "_" not in builtins.__dict__:
@ -55,7 +57,7 @@ resource._register()
provider = Gtk.CssProvider()
provider.load_from_resource("/ar/xjuan/Cambalache/cambalache.css")
display = Gdk.Display.get_default()
Gtk.StyleContext.add_provider_for_display(display, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
Gtk.StyleContext.add_provider_for_display(display, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION - 1)
# FIXME: this is needed in flatpak for icons to work
Gtk.IconTheme.get_for_display(display).add_search_path("/app/share/icons")
@ -78,6 +80,7 @@ from .cmb_objects_base import CmbBaseObject
from .cmb_css import CmbCSS
from .cmb_ui import CmbUI
from .cmb_object import CmbObject
from .cmb_gresource import CmbGResource
# from .cmb_object_data import CmbObjectData
from .cmb_property import CmbProperty
@ -88,12 +91,15 @@ from .cmb_project import CmbProject
from .cmb_db_inspector import CmbDBInspector
from .cmb_view import CmbView
from .cmb_column_view import CmbColumnView
from .cmb_list_view import CmbListView
from .cmb_notification import notification_center, CmbNotification, CmbNotificationCenter
from .cmb_notification_list_view import CmbNotificationListView
from .cmb_object_editor import CmbObjectEditor
from .cmb_signal_editor import CmbSignalEditor
from .cmb_ui_editor import CmbUIEditor
from .cmb_ui_requires_editor import CmbUIRequiresEditor
from .cmb_css_editor import CmbCSSEditor
from .cmb_gresource_editor import CmbGResourceEditor
from .cmb_fragment_editor import CmbFragmentEditor
from .cmb_accessible_editor import CmbAccessibleEditor
from .cmb_type_chooser import CmbTypeChooser

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<gresources>
<gresource prefix="/ar/xjuan/Cambalache/app">
<file>metainfo.xml</file>

View File

@ -38,7 +38,25 @@ window.cmb-window label.message {
background-color: rgba(0, 0, 0, .6);
}
window.cmb-window.dark label.message {
window.cmb-window list.notifications {
margin: 1em 2em;
border-radius: 1em;
border: 1px solid var(--secondary-sidebar-border-color);
}
window.cmb-window list.notifications > row {
padding: 1ex;
}
window.cmb-window list.notifications > row:last-child {
border-width: 0;
}
window.cmb-window list.notifications row > revealer > box > box:last-child {
padding: 0 1em;
}
window.cmb-window.dark .message {
color: black;
background-color: rgba(255, 255, 255, .6);
}
@ -95,6 +113,8 @@ CmbAccessibleEditor {
}
CmbCSSEditor,
CmbGResourceEditor,
CmbGResourceFileEditor,
stackswitcher.property-pane {
padding: 4px;
}

View File

@ -42,7 +42,9 @@ class CmbApplication(Adw.Application):
self.add_main_option("version", b"v", GLib.OptionFlags.NONE, GLib.OptionArg.NONE, _("Print version"), None)
self.add_main_option("export-all", b"E", GLib.OptionFlags.NONE, GLib.OptionArg.FILENAME, _("Export project"), None)
self.add_main_option(
"export-all", b"E", GLib.OptionFlags.NONE, GLib.OptionArg.FILENAME, _("Deprecated: Export project"), None
)
def add_new_window(self):
window = CmbWindow(application=self)
@ -179,6 +181,10 @@ class CmbApplication(Adw.Application):
def _on_open_activate(self, action, data):
filename, target_tk = data.unpack()
# FIXME: use nullable parameter
target_tk = target_tk if target_tk else None
filename = filename if filename else None
window = self.props.active_window
if window and window.project is None:
@ -190,7 +196,9 @@ class CmbApplication(Adw.Application):
target_tk, filename, uipath = data.unpack()
# FIXME: use nullable parameter
target_tk = target_tk if target_tk else None
filename = filename if filename else None
uipath = uipath if uipath else None
window = self.props.active_window
@ -245,10 +253,7 @@ class CmbApplication(Adw.Application):
return 0
if options.contains("export-all"):
filename = options.lookup_value("export-all")
filename = "".join([chr(c) for c in filename.unpack()])
project = CmbProject(filename=filename)
project.export()
print("Export has been deprecated and does nothing. Every UI file is updated on project save.")
return 0
return -1

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.1 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_shortcuts.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->
@ -15,32 +15,56 @@
<property name="view">shortcuts</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;n</property>
<property name="accelerator">&lt;primary&gt;n</property>
<property name="title" translatable="yes">Create new project</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;o</property>
<property name="accelerator">&lt;primary&gt;o</property>
<property name="title" translatable="yes">Open a project</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;w</property>
<property name="accelerator">&lt;primary&gt;i</property>
<property name="title" translatable="yes">Import file</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;primary&gt;s</property>
<property name="title" translatable="yes">Save project</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;primary&gt;&lt;shift&gt;s</property>
<property name="title" translatable="yes">Save project as</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;primary&gt;w</property>
<property name="title" translatable="yes">Close the project</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkShortcutsGroup">
<property name="title" translatable="yes">General</property>
<property name="view">general</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;s</property>
<property name="title" translatable="yes">Save the project</property>
<property name="accelerator">&lt;primary&gt;question</property>
<property name="title" translatable="yes">Keyboard Shortcuts</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;e</property>
<property name="title" translatable="yes">Save and Export</property>
<property name="accelerator">&lt;primary&gt;q</property>
<property name="title" translatable="yes">Quit application</property>
</object>
</child>
</object>
@ -51,25 +75,31 @@
<property name="view">shortcuts</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;Insert</property>
<property name="accelerator">Delete</property>
<property name="title" translatable="yes">Delete object</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;primary&gt;Insert</property>
<property name="title" translatable="yes">Add slot/column</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;Delete</property>
<property name="accelerator">&lt;primary&gt;Delete</property>
<property name="title" translatable="yes">Remove slot/column</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;&lt;shift&gt;Insert</property>
<property name="accelerator">&lt;primary&gt;&lt;shift&gt;Insert</property>
<property name="title" translatable="yes">Add row</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;&lt;shift&gt;Delete</property>
<property name="accelerator">&lt;primary&gt;&lt;shift&gt;Delete</property>
<property name="title" translatable="yes">Remove row</property>
</object>
</child>

View File

@ -72,13 +72,6 @@ intro = [
(_("Try adding a grid"), "intro_button", 3, "add-grid"),
(_("and a button"), "intro_button", 3, "add-button"),
(_("Quite easy! Isn't it?"), "intro_button", 3),
(
_("Once you finish, you can export all UI files to xml here"),
_("Export all"),
5,
None,
CmbTutorPosition.LEFT,
),
(
_("If you have any question, contact us on Matrix!"),
_("Contact"),

View File

@ -31,10 +31,28 @@ from gi.repository import GLib, GObject, Gio, Gdk, Gtk, Pango, Adw
from .cmb_tutor import CmbTutor, CmbTutorState
from . import cmb_tutorial
from cambalache import CmbProject, CmbUI, CmbCSS, CmbObject, CmbTypeChooserPopover, getLogger, config, utils, _
from cambalache import (
CmbProject,
CmbUI,
CmbCSS,
CmbObject,
CmbGResource,
CmbGResourceEditor,
CmbTypeChooserPopover,
getLogger,
notification_center,
config,
utils,
_,
N_
)
from cambalache.cmb_blueprint import CmbBlueprintError
logger = getLogger(__name__)
GObject.type_ensure(CmbGResourceEditor.__gtype__)
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/app/cmb_window.ui")
class CmbWindow(Adw.ApplicationWindow):
@ -46,8 +64,13 @@ class CmbWindow(Adw.ApplicationWindow):
}
open_filter = Gtk.Template.Child()
gtk3_import_filter = Gtk.Template.Child()
gtk4_import_filter = Gtk.Template.Child()
gtk4_filter = Gtk.Template.Child()
gtk3_filter = Gtk.Template.Child()
gtk_builder_filter = Gtk.Template.Child()
blueprint_filter = Gtk.Template.Child()
glade_filter = Gtk.Template.Child()
css_filter = Gtk.Template.Child()
gresource_filter = Gtk.Template.Child()
headerbar = Gtk.Template.Child()
title = Gtk.Template.Child()
@ -59,6 +82,9 @@ class CmbWindow(Adw.ApplicationWindow):
# Start screen
version_label = Gtk.Template.Child()
front_notification_list_view = Gtk.Template.Child()
notification_dialog = Gtk.Template.Child()
notification_list_view = Gtk.Template.Child()
# New Project
np_name_entry = Gtk.Template.Child()
@ -73,8 +99,10 @@ class CmbWindow(Adw.ApplicationWindow):
message_label = Gtk.Template.Child()
# Workspace
workspace_stack = Gtk.Template.Child()
view = Gtk.Template.Child()
column_view = Gtk.Template.Child()
source_view = Gtk.Template.Child()
list_view = Gtk.Template.Child()
type_chooser = Gtk.Template.Child()
editor_stack = Gtk.Template.Child()
ui_editor = Gtk.Template.Child()
@ -86,6 +114,7 @@ class CmbWindow(Adw.ApplicationWindow):
accessible_editor = Gtk.Template.Child()
signal_editor = Gtk.Template.Child()
css_editor = Gtk.Template.Child()
gresource_editor = Gtk.Template.Child()
# Tutor widgets
intro_button = Gtk.Template.Child()
@ -98,7 +127,7 @@ class CmbWindow(Adw.ApplicationWindow):
FULLSCREEN = 1 << 4
__portal_access_msg = _(
"Cambalache will not have access to the file for export because it is outside of your home directory. "
"Cambalache will not have access to the file for save because it is outside of your home directory. "
"Consider using Flatseal or flatpak to give permissions to the host directory."
)
@ -109,6 +138,21 @@ class CmbWindow(Adw.ApplicationWindow):
super().__init__(**kwargs)
self.gtk4_import_filters = Gio.ListStore()
for filter in [
self.gtk4_filter,
self.gtk_builder_filter,
self.blueprint_filter,
self.css_filter,
self.gresource_filter
]:
self.gtk4_import_filters.append(filter)
self.gtk3_import_filters = Gio.ListStore()
for filter in [self.gtk3_filter, self.gtk_builder_filter, self.glade_filter, self.css_filter, self.gresource_filter]:
self.gtk3_import_filters.append(filter)
self.__recent_manager = self.__get_recent_manager()
self.editor_stack.set_size_request(420, -1)
@ -118,6 +162,7 @@ class CmbWindow(Adw.ApplicationWindow):
for action in [
"about",
"add_css",
"add_gresource",
"add_object",
"add_object_toplevel",
"add_placeholder",
@ -132,12 +177,12 @@ class CmbWindow(Adw.ApplicationWindow):
"debug",
"delete",
"donate",
"export",
"import",
"inspect",
"intro",
"liberapay",
"new",
"notification",
"open",
"paste",
"patreon",
@ -178,7 +223,6 @@ class CmbWindow(Adw.ApplicationWindow):
("win.save", ["<Primary>s"]),
("win.save_as", ["<Shift><Primary>s"]),
("win.import", ["<Primary>i"]),
("win.export", ["<Primary>e"]),
("win.close", ["<Primary>w"]),
("win.undo", ["<Primary>z"]),
("win.redo", ["<Primary><shift>z"]),
@ -279,6 +323,10 @@ class CmbWindow(Adw.ApplicationWindow):
self.__recent_manager.connect("changed", lambda rm: self.__update_recent_menu())
self.__update_recent_menu()
self.notification_list_view.notification_center = notification_center
self.front_notification_list_view.notification_center = notification_center
notification_center.connect("new-notification", self.__on_new_notification)
def __get_recent_manager(self):
# Load the user host recently used file
if os.environ.get("container", None) == "flatpak":
@ -301,12 +349,13 @@ class CmbWindow(Adw.ApplicationWindow):
if self.__project:
self.__project.disconnect_by_func(self.__on_project_filename_notify)
self.__project.disconnect_by_func(self.__on_project_selection_changed)
self.__project.disconnect_by_func(self.__on_project_gresource_changed)
self.__project.disconnect_by_func(self.__on_project_changed)
self.__project = project
self.view.project = project
self.type_chooser.project = project
self.column_view.project = project
self.list_view.project = project
# Clear Editors
self.ui_editor.object = None
@ -321,6 +370,7 @@ class CmbWindow(Adw.ApplicationWindow):
if project:
self.__project.connect("notify::filename", self.__on_project_filename_notify)
self.__project.connect("selection-changed", self.__on_project_selection_changed)
self.__project.connect("gresource-changed", self.__on_project_gresource_changed)
self.__project.connect("changed", self.__on_project_changed)
self.__on_project_selection_changed(project)
@ -403,20 +453,6 @@ class CmbWindow(Adw.ApplicationWindow):
)
popover.popup()
@Gtk.Template.Callback("on_ui_editor_remove_ui")
def __on_ui_editor_remove_ui(self, editor, ui):
self.__remove_object_with_confirmation(ui)
@Gtk.Template.Callback("on_ui_editor_export_ui")
def __on_ui_editor_export_ui(self, editor, ui):
if ui.export():
self._show_message(_("File Exported"))
@Gtk.Template.Callback("on_css_editor_remove_ui")
def __on_css_editor_remove_ui(self, editor):
self.__remove_object_with_confirmation(editor.object)
return True
def __on_focus_widget_notify(self, obj, pspec):
widget = self.props.focus_widget
@ -436,6 +472,7 @@ class CmbWindow(Adw.ApplicationWindow):
sensitive = len(editable.get_chars(0, -1)) != 0
self.np_location_chooser.set_sensitive(sensitive)
self.np_ui_entry.set_sensitive(sensitive)
self.__update_action_new()
def __update_dark_mode(self, style_manager):
if style_manager.props.dark:
@ -506,26 +543,55 @@ class CmbWindow(Adw.ApplicationWindow):
self.__update_action_undo_redo()
self.__update_action_save()
def __update_gresource_view(self):
if self.workspace_stack.get_visible_child_name() != "gresource":
return
sel = self.project.get_selection()
if len(sel) < 1:
return
obj = sel[0]
if isinstance(obj, CmbGResource):
gresource_id = obj.gresources_bundle.gresource_id
resource_xml = self.project.db.gresource_tostring(gresource_id)
else:
resource_xml = ""
self.source_view.buffer.set_text(resource_xml)
def __on_project_gresource_changed(self, project, gresource, field):
self.__update_gresource_view()
def __on_project_selection_changed(self, project):
sel = project.get_selection()
self.__update_action_clipboard()
obj = sel[0] if len(sel) > 0 else None
if type(obj) is CmbUI:
if isinstance(obj, CmbUI):
self.ui_editor.object = obj
self.ui_requires_editor.object = obj
self.ui_fragment_editor.object = obj
self.workspace_stack.set_visible_child_name("ui")
self.editor_stack.set_visible_child_name("ui")
obj = None
elif type(obj) is CmbObject:
elif isinstance(obj, CmbObject):
self.workspace_stack.set_visible_child_name("ui")
self.editor_stack.set_visible_child_name("object")
if obj:
self.__user_message_by_type(obj.info)
elif type(obj) is CmbCSS:
elif isinstance(obj, CmbCSS):
self.css_editor.object = obj
self.editor_stack.set_visible_child_name("css")
obj = None
elif isinstance(obj, CmbGResource):
self.gresource_editor.object = obj
self.workspace_stack.set_visible_child_name("gresource")
self.editor_stack.set_visible_child_name("gresource")
self.__update_gresource_view()
obj = None
self.object_editor.object = obj
@ -546,6 +612,9 @@ class CmbWindow(Adw.ApplicationWindow):
self.intro_button.set_visible(enabled)
def __update_action_notification(self):
self.actions["notification"].set_enabled(len(notification_center.store) > 0)
def __update_action_add_object(self):
has_project = self.__is_project_visible()
has_selection = True if self.project and len(self.project.get_selection()) > 0 else False
@ -586,24 +655,39 @@ class CmbWindow(Adw.ApplicationWindow):
else:
self.title.remove_css_class("changed")
def __update_action_new(self):
self.actions["new"].set_enabled(len(self.np_name_entry.props.text) > 0)
def __update_actions(self):
has_project = self.__is_project_visible()
for action in ["save_as", "add_ui", "add_css", "delete", "import", "export", "close", "debug"]:
for action in [
"save_as",
"add_ui",
"add_css",
"add_gresource",
"delete",
"import",
"close",
"debug"
]:
self.actions[action].set_enabled(has_project)
self.__update_action_new()
self.__update_action_save()
self.__update_action_intro()
self.__update_action_notification()
self.__update_action_clipboard()
self.__update_action_undo_redo()
self.__update_action_add_object()
self.__update_action_remove_parent()
def __file_open_dialog_new(self, title, filter_obj=None, accept_label=None, use_project_dir=False):
def __file_open_dialog_new(self, title, filter_obj=None, accept_label=None, use_project_dir=False, filters=None):
dialog = Gtk.FileDialog(
modal=True,
title=title,
default_filter=filter_obj,
filters=filters,
accept_label=accept_label,
)
@ -654,9 +738,13 @@ class CmbWindow(Adw.ApplicationWindow):
basename = os.path.basename(filename)
name, ext = os.path.splitext(basename)
if target_tk is None:
if not target_tk:
target_tk = CmbProject.get_target_from_ui_file(filename)
if not target_tk:
self.ask_gtk_version(filename)
return
self.project = CmbProject(filename=os.path.join(dirname, f"{name}.cmb"), target_tk=target_tk)
self.__set_page("workspace")
self.__update_actions()
@ -693,14 +781,14 @@ class CmbWindow(Adw.ApplicationWindow):
first_msg = _("Cambalache encounter the following issues:")
# Translators: this is the last message after the list of unsupported features
last_msg = _("Your file will be exported as '{name}.cmb.ui' to avoid data loss.").format(name=name)
last_msg = _("Your file will be saved as '{name}.cmb.ui' to avoid data loss.").format(name=name)
unsupported_features_list = [first_msg] + list + [last_msg]
else:
unsupported_feature = msg[0]
text = _(
"Cambalache encounter {unsupported_feature}\n"
"Your file will be exported as '{name}.cmb.ui' to avoid data loss."
"Your file will be saved as '{name}.cmb.ui' to avoid data loss."
).format(unsupported_feature=unsupported_feature, name=name)
self.present_message_to_user(
@ -864,7 +952,7 @@ class CmbWindow(Adw.ApplicationWindow):
else:
self.project.redo()
except Exception as e:
logger.warning(f"Undo/Redo error", exc_info=True)
logger.warning("Undo/Redo error", exc_info=True)
self.present_message_to_user(
_("Undo/Redo stack got corrupted"),
secondary_text=_("Please try to reproduce and file an issue\n Error: {msg}").format(msg=str(e))
@ -937,10 +1025,12 @@ class CmbWindow(Adw.ApplicationWindow):
def on_dialog_response(dialog, response):
if response == Gtk.ResponseType.YES:
if type(obj) is CmbUI:
if isinstance(obj, CmbUI):
self.project.remove_ui(obj)
elif type(obj) is CmbCSS:
elif isinstance(obj, CmbCSS):
self.project.remove_css(obj)
elif isinstance(obj, CmbGResource):
self.project.remove_gresource(obj)
dialog.destroy()
@ -968,10 +1058,18 @@ class CmbWindow(Adw.ApplicationWindow):
selection = self.project.get_selection()
for obj in selection:
if type(obj) is CmbObject:
try:
if isinstance(obj, CmbObject):
self.project.remove_object(obj)
elif isinstance(obj, CmbGResource):
if obj.resource_type == "gresources":
self.__remove_object_with_confirmation(obj)
else:
self.project.remove_gresource(obj)
else:
self.__remove_object_with_confirmation(obj)
except Exception as e:
self.present_message_to_user(_("Error deleting {name}").format(name=obj.display_name_type), secondary_text=str(e))
def _on_add_object_activate(self, action, data):
info = self.type_chooser.props.selected_type
@ -1020,13 +1118,13 @@ class CmbWindow(Adw.ApplicationWindow):
first_msg = _("Cambalache encounter the following issues:")
# Translators: this is the last message after the list of unsupported features
last_msg = _("Your file will be exported as '{name}.cmb.ui' to avoid data loss.").format(name=name)
last_msg = _("Your file will be saved as '{name}.cmb.ui' to avoid data loss.").format(name=name)
unsupported_features_list = [first_msg] + list + [last_msg]
else:
unsupported_feature = msg[0]
text = _(
"Cambalache encounter {unsupported_feature}\nYour file will be exported as '{name}.cmb.ui' to avoid data loss."
"Cambalache encounter {unsupported_feature}\nYour file will be saved as '{name}.cmb.ui' to avoid data loss."
).format(unsupported_feature=unsupported_feature, name=name)
self.present_message_to_user(
@ -1042,24 +1140,56 @@ class CmbWindow(Adw.ApplicationWindow):
def dialog_callback(dialog, res):
try:
for file in dialog.open_multiple_finish(res):
path = file.get_path()
content_type = utils.content_type_guess(path)
print("IMPORT", path, content_type)
if content_type in ["application/x-gtk-builder", "application/x-glade", "text/x-blueprint"]:
self.import_file(file.get_path())
elif content_type == "text/css":
self.project.add_css(path)
elif content_type == "application/xml" and path.endswith("gresource.xml"):
self.project.import_gresource(path)
except Exception as e:
logger.warning(f"Error {e}")
if self.project.target_tk == "gtk-4.0":
import_filter = self.gtk4_import_filter
import_filters = self.gtk4_import_filters
else:
import_filter = self.gtk3_import_filter
import_filters = self.gtk3_import_filters
dialog = self.__file_open_dialog_new(
_("Choose file to import"),
filter_obj=import_filter,
filters=import_filters,
accept_label=_("Import")
)
dialog.open_multiple(self, None, dialog_callback)
def _on_add_gresource_activate(self, action, data):
if self.project is None:
return
gresource = self.project.add_gresource("gresources")
self.project.set_selection([gresource])
def __save(self):
if self.project.save():
retval = False
try :
retval = self.project.save()
except CmbBlueprintError as e:
self.present_message_to_user(
_("Error saving project"),
secondary_text=N_(
"blueprintcompiler encounter the following error:",
"blueprintcompiler encounter the following errors:",
len(e.errors)
),
details=[str(e)]
)
finally:
if retval:
self.__last_saved_index = self.project.history_index
self.__update_action_save()
self.emit("project-saved", self.project)
@ -1078,16 +1208,6 @@ class CmbWindow(Adw.ApplicationWindow):
return False
def _on_export_activate(self, action, data):
if self.project is None:
return
self.save_project()
n = self.project.export()
self._show_message(_("{n} files exported").format(n=n) if n > 1 else _("File exported"))
def _close_project_dialog_new(self):
text = _("Save changes before closing?")
dialog = Gtk.MessageDialog(
@ -1187,20 +1307,19 @@ class CmbWindow(Adw.ApplicationWindow):
about.props.translator_credits = "\n".join(translator_list)
def _on_about_activate(self, action, data):
about = Adw.AboutWindow.new_from_appdata("/ar/xjuan/Cambalache/app/metainfo.xml", config.VERSION)
about = Adw.AboutDialog.new_from_appdata("/ar/xjuan/Cambalache/app/metainfo.xml", config.VERSION)
about.props.transient_for = self
about.props.artists = [
"Franco Dodorico",
"Juan Pablo Ugarte",
]
about.props.copyright = "© 2020-2024 Juan Pablo Ugarte"
about.props.copyright = "© 2020-2025 Juan Pablo Ugarte"
about.props.license_type = Gtk.License.LGPL_2_1_ONLY
self.__update_translators(about)
self.__populate_supporters(about)
about.present()
about.present(self)
def _on_add_parent_activate(self, action, data):
obj = self.project.get_selection()[0]
@ -1295,7 +1414,7 @@ class CmbWindow(Adw.ApplicationWindow):
elif node in ["add-ui", "add-window", "add-grid", "add-button"]:
self.tutor_waiting_for_user_action = True
self.tutor.pause()
elif node in ["donate", "export_all"]:
elif node in ["donate"]:
self.menu_button.popdown()
elif node == "show-type-popover":
widget.props.popover.popdown()
@ -1424,4 +1543,13 @@ class CmbWindow(Adw.ApplicationWindow):
else:
self.message_revealer.props.reveal_child = False
def __notification_present(self):
if self.stack.get_visible_child_name() != "cambalache":
self.notification_dialog.present(self)
def _on_notification_activate(self, action, data):
self.__notification_present()
def __on_new_notification(self, center, notification):
self.__notification_present()
self.__update_action_notification()

View File

@ -1,41 +1,45 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.93.0 -->
<!-- Created with Cambalache 0.97.1 -->
<interface>
<!-- interface-name cmb_window.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->
<requires lib="gio" version="2.0"/>
<requires lib="gtk" version="4.14"/>
<requires lib="libadwaita" version="1.5"/>
<object class="GtkFileFilter" id="gtk4_import_filter">
<property name="mime-types">application/x-gtk-builder</property>
</object>
<menu id="main_menu">
<item>
<attribute name="action">win.create_new</attribute>
<attribute name="label">New Project</attribute>
<attribute name="label" translatable="yes">New Project</attribute>
</item>
<section>
<submenu>
<attribute name="label" translatable="yes">Add file</attribute>
<item>
<attribute name="action">win.import</attribute>
<attribute name="label" translatable="yes">Import UI</attribute>
</item>
<item>
<attribute name="action">win.export</attribute>
<attribute name="label" translatable="yes">Export all UI</attribute>
<attribute name="action">win.add_ui</attribute>
<attribute name="label" translatable="yes">Add UI</attribute>
</item>
<item>
<attribute name="action">win.add_css</attribute>
<attribute name="label" translatable="yes">Add CSS file</attribute>
<attribute name="label" translatable="yes">Add CSS</attribute>
</item>
<item>
<attribute name="action">win.add_gresource</attribute>
<attribute name="label" translatable="yes">Add GResource</attribute>
</item>
</submenu>
<item>
<attribute name="action">win.import</attribute>
<attribute name="label" translatable="yes">Import file</attribute>
</item>
</section>
<section>
<item>
<attribute name="action">win.save</attribute>
<attribute name="label">Save</attribute>
<attribute name="label" translatable="yes">Save</attribute>
</item>
<item>
<attribute name="action">win.save_as</attribute>
<attribute name="label">Save As</attribute>
<attribute name="label" translatable="yes">Save As</attribute>
</item>
<item>
<attribute name="action">win.close</attribute>
@ -47,6 +51,10 @@
<attribute name="action">win.intro</attribute>
<attribute name="label" translatable="yes">Interactive intro</attribute>
</item>
<item>
<attribute name="action">win.notification</attribute>
<attribute name="label" translatable="yes">Notifications</attribute>
</item>
<item>
<attribute name="action">win.contact</attribute>
<attribute name="label" translatable="yes">Contact</attribute>
@ -88,7 +96,7 @@
<object class="AdwSplitButton" id="open_button">
<property name="action-name">win.open</property>
<property name="dropdown-tooltip">Recent Projects</property>
<property name="label">_Open</property>
<property name="label" translatable="yes">_Open</property>
<property name="menu-model">recent_menu</property>
<property name="use-underline">True</property>
</object>
@ -169,13 +177,19 @@
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="CmbNotificationListView" id="front_notification_list_view">
<property name="halign">start</property>
<property name="valign">center</property>
<property name="vexpand">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="version_label">
<property name="halign">center</property>
<property name="margin-bottom">4</property>
<property name="name">version</property>
<property name="valign">end</property>
<property name="vexpand">1</property>
<attributes>
<attribute name="size" value="9000"/>
</attributes>
@ -245,7 +259,7 @@
<child>
<object class="GtkEntry" id="np_name_entry">
<property name="focusable">1</property>
<property name="input-hints">GTK_INPUT_HINT_LOWERCASE | GTK_INPUT_HINT_NONE</property>
<property name="input-hints">lowercase|none</property>
<property name="input-purpose">alpha</property>
<property name="placeholder-text" translatable="yes">&lt;project basename&gt;</property>
<property name="width-chars">32</property>
@ -395,7 +409,7 @@
<child>
<object class="GtkEntry" id="np_ui_entry">
<property name="focusable">1</property>
<property name="input-hints">GTK_INPUT_HINT_LOWERCASE | GTK_INPUT_HINT_NONE</property>
<property name="input-hints">lowercase|none</property>
<property name="input-purpose">alpha</property>
<property name="sensitive">0</property>
<property name="width-chars">32</property>
@ -534,8 +548,6 @@
<object class="CmbUIEditor" id="ui_editor">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<signal name="remove-ui" handler="on_ui_editor_remove_ui"/>
<signal name="export-ui" handler="on_ui_editor_export_ui"/>
</object>
</property>
<property name="name">properties</property>
@ -578,13 +590,21 @@
<object class="CmbCSSEditor" id="css_editor">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<signal name="remove-css" handler="on_css_editor_remove_ui"/>
</object>
</property>
<property name="name">css</property>
<property name="title" translatable="yes">CSS Editor</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="child">
<object class="CmbGResourceEditor" id="gresource_editor"/>
</property>
<property name="name">gresource</property>
<property name="title">GResource Editor</property>
</object>
</child>
</object>
</property>
<property name="focusable">1</property>
@ -595,11 +615,16 @@
<property name="start-child">
<object class="GtkPaned">
<property name="end-child">
<object class="GtkStack" id="workspace_stack">
<child>
<object class="GtkStackPage">
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="CmbTypeChooser" id="type_chooser">
<property name="spacing">2</property>
<property name="valign">start</property>
<signal name="chooser-popdown" handler="on_type_chooser_chooser_popdown"/>
<signal name="chooser-popup" handler="on_type_chooser_chooser_popup"/>
<signal name="type-selected" handler="on_type_chooser_type_selected"/>
@ -614,6 +639,26 @@
</child>
</object>
</property>
<property name="name">ui</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="child">
<object class="GtkScrolledWindow">
<property name="child">
<object class="CmbSourceView" id="source_view">
<property name="editable">False</property>
<property name="lang">xml</property>
</object>
</property>
</object>
</property>
<property name="name">gresource</property>
</object>
</child>
</object>
</property>
<property name="focusable">1</property>
<property name="resize-start-child">0</property>
<property name="shrink-end-child">0</property>
@ -627,7 +672,7 @@
<property name="propagate-natural-height">True</property>
<property name="propagate-natural-width">True</property>
<child>
<object class="CmbColumnView" id="column_view"/>
<object class="CmbListView" id="list_view"/>
</child>
</object>
</child>
@ -774,9 +819,7 @@
<property name="halign">center</property>
<property name="transition-type">slide-up</property>
<property name="valign">end</property>
<style>
<class name="message"/>
</style>
<style/>
</object>
</child>
</object>
@ -787,8 +830,48 @@
<class name="cmb-window"/>
</style>
</template>
<object class="GtkFileFilter" id="gtk3_import_filter">
<object class="GtkFileFilter" id="gtk_builder_filter">
<property name="mime-types">application/x-gtk-builder</property>
</object>
<object class="GtkFileFilter" id="glade_filter">
<property name="mime-types">application/x-glade</property>
</object>
<object class="GtkFileFilter" id="blueprint_filter">
<property name="mime-types">text/x-blueprint</property>
</object>
<object class="GtkFileFilter" id="css_filter">
<property name="mime-types">text/css</property>
</object>
<object class="GtkFileFilter" id="gresource_filter">
<property name="suffixes">gresource.xml</property>
</object>
<object class="GtkFileFilter" id="gtk4_filter">
<property name="mime-types">application/x-gtk-builder
application/x-glade</property>
text/x-blueprint
text/css</property>
<property name="name">All supported files</property>
<property name="suffixes">gresource.xml</property>
</object>
<object class="GtkFileFilter" id="gtk3_filter">
<property name="mime-types">application/x-gtk-builder
application/x-glade
text/css</property>
<property name="name">All supported files</property>
<property name="suffixes">gresource.xml</property>
</object>
<object class="AdwDialog" id="notification_dialog">
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="AdwHeaderBar"/>
</child>
<child>
<object class="CmbNotificationListView" id="notification_list_view"/>
</child>
</object>
</property>
<property name="follows-content-size">True</property>
<property name="title" translatable="yes">Notifications</property>
</object>
</interface>

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,8 @@ CmbPropertyLabel.hidden:hover > box > image {
opacity: 1;
}
popover.cmb-binding-popover button.close {
popover.cmb-binding-popover button.close,
list.notifications button.close {
padding: unset;
margin: unset;
border: unset;
@ -84,10 +85,47 @@ CmbPropertyLabel.warning > box > label {
text-decoration: underline wavy @warning_color;
}
row.drop-before:drop(active) {
box-shadow: 0px 1px 0px @theme_selected_bg_color inset;
listview.cmb-list-view {
background-color: @theme_bg_color;
}
row.drop-after:drop(active) {
box-shadow: 0px -1px 0px @theme_selected_bg_color inset;
listview.cmb-list-view > row {
padding: 2px 8px;
min-height: 30px;
}
listview.cmb-list-view > row:drop(active):not(.drop-after):not(.drop-before) {
outline: 2px solid color-mix(in srgb, @theme_bg_color 80%, black);
outline-offset: -4px;
}
listview.cmb-list-view > row.drop-before:drop(active) {
border: 0;
border-radius: 0;
border-top: 2px solid color-mix(in srgb, @theme_bg_color 80%, black);
margin-top: -2px;
}
listview.cmb-list-view > row.drop-after:drop(active) {
border: 0;
border-radius: 0;
border-bottom: 2px solid color-mix(in srgb, @theme_bg_color 80%, black);
margin-bottom: 0;
}
listview.cmb-list-view > row > treeexpander.cmb-path > expander {
-gtk-icon-source: -gtk-icontheme("folder-symbolic");
}
listview.cmb-list-view > row > treeexpander.cmb-path > expander:checked {
-gtk-icon-source: -gtk-icontheme("folder-open-symbolic");
}
listview.cmb-list-view > row > treeexpander.cmb-unsaved-path > expander {
-gtk-icon-source: -gtk-icontheme("view-list-symbolic");
}
button.compact {
padding: 2px 4px;
font-weight: normal;
}

View File

@ -1,22 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<gresources>
<gresource prefix="/ar/xjuan/Cambalache">
<file>control/cmb_translatable_widget.ui</file>
<file>db/cmb_base.sql</file>
<file>db/cmb_project.sql</file>
<file>db/cmb_history.sql</file>
<file>cmb_view.ui</file>
<file>cambalache.css</file>
<file>cmb_context_menu.ui</file>
<file>cmb_css_editor.ui</file>
<file>cmb_db_inspector.ui</file>
<file>cmb_fragment_editor.ui</file>
<file>cmb_gresource_editor.ui</file>
<file>cmb_notification_list_view.ui</file>
<file>cmb_version_notification_view.ui</file>
<file>cmb_message_notification_view.ui</file>
<file>cmb_poll_option_check.ui</file>
<file>cmb_poll_notification_view.ui</file>
<file>cmb_object_data_editor.ui</file>
<file>cmb_signal_editor.ui</file>
<file>cmb_type_chooser.ui</file>
<file>cmb_type_chooser_widget.ui</file>
<file>cmb_ui_editor.ui</file>
<file>cmb_css_editor.ui</file>
<file>cmb_fragment_editor.ui</file>
<file>cmb_object_data_editor.ui</file>
<file>cmb_signal_editor.ui</file>
<file>cmb_db_inspector.ui</file>
<file>cambalache.css</file>
<file>icons/scalable/actions/bind-symbolic.svg</file>
<file>cmb_view.ui</file>
<file>control/cmb_file_button.ui</file>
<file>control/cmb_translatable_widget.ui</file>
<file>db/cmb_base.sql</file>
<file>db/cmb_history.sql</file>
<file>db/cmb_project.sql</file>
<file>icons/scalable/actions/binded-symbolic.svg</file>
<file>icons/scalable/actions/bind-symbolic.svg</file>
<file>cmb_notification_list_row.ui</file>
</gresource>
</gresources>

View File

@ -0,0 +1,86 @@
#
# Blueprint compiler integration functions
#
# Copyright (C) 2025 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
import io
try:
import blueprintcompiler as bp
from blueprintcompiler import parser, tokenizer
from blueprintcompiler.decompiler import decompile_string
from blueprintcompiler.outputs import XmlOutput
except Exception:
bp = None
class CmbBlueprintError(Exception):
def __init__(self, message, errors=[]):
super().__init__(message)
self.errors = errors
class CmbBlueprintUnsupportedError(CmbBlueprintError):
pass
class CmbBlueprintMissingError(CmbBlueprintError):
def __init__(self):
super().__init__("blueprintcompiler is not available")
def cmb_blueprint_decompile(data: str) -> str:
if bp is None:
raise CmbBlueprintMissingError()
try:
retval = decompile_string(data)
except bp.decompiler.UnsupportedError as e:
raise CmbBlueprintUnsupportedError(str(e))
except Exception as e:
raise CmbBlueprintError(str(e))
return retval
def cmb_blueprint_compile(data: str) -> str:
if bp is None:
raise CmbBlueprintMissingError()
tokens = tokenizer.tokenize(data)
ast, errors, warnings = parser.parse(tokens)
if errors:
f = io.StringIO("")
errors.pretty_print("temp", data, f)
f.seek(0)
raise CmbBlueprintError(f.read(), errors=errors)
if ast is None:
raise CmbBlueprintError("AST is None")
# Ignore warnings
retval = XmlOutput().emit(ast)
return retval.encode()

View File

@ -1,524 +0,0 @@
#
# CmbColumnView
#
# Copyright (C) 2024 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from gi.repository import GObject, Gdk, Gtk
from .cmb_ui import CmbUI
from .cmb_object import CmbObject
from .cmb_context_menu import CmbContextMenu
from .cmb_project import CmbProject
from cambalache import _
class CmbColumnView(Gtk.ColumnView):
__gtype_name__ = "CmbColumnView"
def __init__(self, **kwargs):
self.__project = None
self.__tree_model = None
self.__in_selection_change = False
self.single_selection = Gtk.SingleSelection()
super().__init__(**kwargs)
self.props.has_tooltip = True
self.props.hexpand = True
self.props.show_row_separators = False
self.props.show_column_separators = False
self.props.reorderable = False
self.__add_column("display-name")
self.single_selection.connect("notify::selected-item", self.__on_selected_item_notify)
self.set_model(self.single_selection)
gesture = Gtk.GestureClick(button=Gdk.BUTTON_SECONDARY)
gesture.connect("pressed", self.__on_button_press)
self.add_controller(gesture)
self.connect("activate", self.__on_activate)
def __add_column(self, property_id):
factory = Gtk.SignalListItemFactory()
factory.connect("setup", self._on_factory_setup)
factory.connect("bind", self._on_factory_bind, property_id)
factory.connect("unbind", self._on_factory_unbind)
column = Gtk.ColumnViewColumn(factory=factory, expand=True)
self.append_column(column)
# FIXME: Add api to Gtk to hide column title widget
column_view = column.get_column_view()
child = column_view.get_first_child()
if GObject.type_name(child.__gtype__) == "GtkColumnViewRowWidget":
child.set_visible(False)
def __on_button_press(self, gesture, npress, x, y):
expander = self.__get_tree_expander(x, y)
if expander is None or npress != 1:
return False
# Select row at x,y
list_row = expander.get_list_row()
self.single_selection.set_selected(list_row.get_position())
menu = CmbContextMenu()
if self.__project:
menu.target_tk = self.__project.target_tk
menu.set_parent(self)
menu.popup_at(x, y)
return True
def __get_object_ancestors(self, obj):
if isinstance(obj, CmbObject):
ancestors = {obj.ui}
parent = obj.parent
while parent:
ancestors.add(parent)
parent = parent.parent
return ancestors
# CmbUI and CmbCSS do not have ancestors
return {}
def __object_ancestor_expand(self, obj):
ancestors = self.__get_object_ancestors(obj)
i = 0
# Iterate over tree model
# NOTE: only visible/expanded rows are returned
list_row = self.__tree_model.get_row(i)
while list_row:
item = list_row.get_item()
# Return position if we reached the object row
if item == obj:
return i
elif item in ancestors:
# Expand row if its part of the hierarchy
list_row.set_expanded(True)
i += 1
list_row = self.__tree_model.get_row(i)
return None
def __on_project_selection_changed(self, p):
list_row = self.single_selection.get_selected_item()
current_selection = [list_row.get_item()] if list_row else []
selection = self.__project.get_selection()
if selection == current_selection:
return
self.__in_selection_change = True
if len(selection) > 0:
position = self.__object_ancestor_expand(selection[0])
if position is not None:
self.single_selection.select_item(position, True)
else:
self.single_selection.unselect_all()
else:
self.single_selection.unselect_all()
self.__in_selection_change = False
@GObject.Property(type=CmbProject)
def project(self):
return self.__project
@project.setter
def _set_project(self, project):
if self.__project:
self.__project.disconnect_by_func(self.__on_project_selection_changed)
self.__project = project
if project:
self.__tree_model = Gtk.TreeListModel.new(
project,
False,
False,
self.__tree_model_create_func,
None
)
self.single_selection.props.model = self.__tree_model
self.__project.connect("selection-changed", self.__on_project_selection_changed)
else:
self.single_selection.props.model = None
def __tree_model_create_func(self, item, data):
if isinstance(item, CmbObject):
return item
elif isinstance(item, CmbUI):
return item
return None
def __on_selected_item_notify(self, single_selection, pspec):
if self.__in_selection_change or self.__project is None:
return
list_item = single_selection.get_selected_item()
if list_item:
item = list_item.get_item()
self.__project.set_selection([item])
else:
self.__project.set_selection([])
def __on_item_notify(self, item, pspec, label):
self.__update_label(item, label, pspec.name)
def __update_label(self, item, label, property_id):
val = str(item.get_property(property_id))
label.set_markup(val if val else "")
def _on_factory_setup(self, factory, list_item):
expander = Gtk.TreeExpander()
label = Gtk.Inscription(hexpand=True)
expander.set_child(label)
list_item.set_child(expander)
def __on_list_store_n_items_notify(self, list_store, pspec, expander):
expander.props.hide_expander = list_store.props.n_items == 0
def __drop_target_new(self):
drop_target = Gtk.DropTarget.new(
type=GObject.TYPE_NONE, actions=Gdk.DragAction.COPY
)
drop_target.set_gtypes([CmbObject, CmbUI])
return drop_target
def _on_factory_bind(self, factory, list_item, property_id):
row = list_item.get_item()
expander = list_item.get_child()
row_widget = expander.props.parent.props.parent
expander.set_list_row(row)
item = row.get_item()
label = expander.get_child()
# ensure drag&drop variables
row_widget._drop_target = None
row_widget._drag_source = None
expander._drop_target = None
# Handle label
self.__update_label(item, label, property_id)
item.connect(f"notify::{property_id}", self.__on_item_notify, label)
# Add controllers and drag sources
if isinstance(item, CmbObject):
# Drag source, only objects can be dragged
drag_source = Gtk.DragSource()
drag_source.connect("prepare", self.__on_drag_prepare)
drag_source.connect("drag-begin", self.__on_drag_begin)
row_widget.add_controller(drag_source)
row_widget._drag_source = drag_source
# Expander Drop target
drop_target = self.__drop_target_new()
drop_target.connect("accept", self.__on_expander_drop_accept)
drop_target.connect("drop", self.__on_expander_drop_drop)
expander.add_controller(drop_target)
expander._drop_target = drop_target
# Row Drop target
drop_target = self.__drop_target_new()
drop_target.connect("accept", self.__on_row_drop_accept)
drop_target.connect("motion", self.__on_row_drop_motion)
drop_target.connect("drop", self.__on_row_drop_drop)
row_widget.add_controller(drop_target)
row_widget._drop_target = drop_target
elif isinstance(item, CmbUI):
# Expander Drop target
drop_target = self.__drop_target_new()
drop_target.connect("accept", self.__on_ui_expander_drop_accept)
drop_target.connect("drop", self.__on_ui_expander_drop_drop)
expander.add_controller(drop_target)
expander._drop_target = drop_target
# Row Drop target
drop_target = self.__drop_target_new()
drop_target.connect("accept", self.__on_ui_row_drop_accept)
drop_target.connect("drop", self.__on_ui_row_drop_drop)
row_widget.add_controller(drop_target)
row_widget.__drop_target = drop_target
else:
expander.props.hide_expander = True
return
expander.props.hide_expander = item.props.n_items == 0
item.connect("notify::n-items", self.__on_list_store_n_items_notify, expander)
def _on_factory_unbind(self, factory, list_item):
row = list_item.get_item()
item = row.get_item()
expander = list_item.get_child()
item.disconnect_by_func(self.__on_item_notify)
if isinstance(item, CmbObject) or isinstance(item, CmbUI):
item.disconnect_by_func(self.__on_list_store_n_items_notify)
if expander is None:
return
# Clear controllers
if expander._drop_target:
expander.remove_controller(expander._drop_target)
expander._drop_target = None
row_widget = expander.props.parent.props.parent
if row_widget:
if row_widget._drop_target:
row_widget.remove_controller(row_widget._drop_target)
row_widget._drop_target = None
if row_widget._drag_source:
row_widget.remove_controller(row_widget._drag_source)
row_widget._drag_source = None
def __get_item_from_target(self, target):
target_widget = target.get_widget()
if isinstance(target_widget, Gtk.TreeExpander):
expander = target_widget
else:
cell = target_widget.get_first_child()
expander = cell.get_first_child()
list_row = expander.get_list_row()
item = list_row.get_item()
return item
def __on_drag_prepare(self, drag_source, x, y):
item = self.__get_item_from_target(drag_source)
return Gdk.ContentProvider.new_for_value(item)
def __on_drag_begin(self, drag_source, drag):
expander = drag_source.get_widget().get_first_child().get_first_child()
drag._item = self.__get_item_from_target(drag_source)
drag_source.set_icon(Gtk.WidgetPaintable.new(expander.get_first_child()), 0, 0)
def __get_drop_before(self, widget, x, y):
return True if y < widget.get_height()/2 else False
def __ui_drop_accept(self, drop, item):
origin_item = drop.get_drag()._item
if origin_item == item:
return False
# Ignore if its the same UI and item is already a toplevel
if origin_item.ui_id == item.ui_id and origin_item.parent_id is None:
return False
return True
def __on_ui_expander_drop_accept(self, target, drop):
item = self.__get_item_from_target(target)
return self.__ui_drop_accept(drop, item)
def __on_ui_row_drop_accept(self, target, drop):
item = self.__get_item_from_target(target)
return self.__ui_drop_accept(drop, item)
def __on_object_drop_accept(self, drop, item):
origin_item = drop.get_drag()._item
if origin_item == item:
return None
if not isinstance(item, CmbObject):
return None
# Ignore if its the same parent
if origin_item.parent_id == item.object_id:
return None
return origin_item
def __on_expander_drop_accept(self, target, drop):
item = self.__get_item_from_target(target)
origin_item = self.__on_object_drop_accept(drop, item)
if origin_item is None:
return False
return self.__project._check_can_add(origin_item.type_id, item.type_id)
def __on_row_drop_accept(self, target, drop):
item = self.__get_item_from_target(target)
origin_item = self.__on_object_drop_accept(drop, item)
if origin_item is None or item.parent is None:
return False
return self.__project._check_can_add(origin_item.type_id, item.parent.type_id)
def __on_row_drop_motion(self, target, x, y):
row_widget = target.get_widget()
drop_before = self.__get_drop_before(row_widget, x, y)
row_widget.remove_css_class("drop-before")
row_widget.remove_css_class("drop-after")
if drop_before:
row_widget.add_css_class("drop-before")
else:
row_widget.add_css_class("drop-after")
return Gdk.DragAction.COPY
def __on_drop_drop(self, origin_item, item):
if not isinstance(item, CmbUI):
return
if origin_item.ui_id == item.ui_id:
self.__project.history_push(_("Move {name} as toplevel").format(name=origin_item.display_name))
origin_item.parent_id = 0
self.__project.history_pop()
else:
# TODO: Use copy/paste to move across UI files
pass
def __on_ui_row_drop_drop(self, target, origin_item, x, y):
item = self.__get_item_from_target(target)
self.__on_drop_drop(origin_item, item)
def __on_ui_expander_drop_drop(self, target, origin_item, x, y):
item = self.__get_item_from_target(target)
self.__on_drop_drop(origin_item, item)
def __on_expander_drop_drop(self, target, origin_item, x, y):
item = self.__get_item_from_target(target)
if not isinstance(item, CmbObject):
return
# TODO: handle dragging from one UI to another
if origin_item.ui_id != item.ui_id:
return
if origin_item.parent_id != item.object_id:
self.__project.history_push(
_("Move {name} to {target}").format(name=origin_item.display_name, target=item.display_name)
)
origin_item.parent_id = item.object_id
self.__project.history_pop()
def __on_row_drop_drop(self, target, origin_item, x, y):
row_widget = target.get_widget()
item = self.__get_item_from_target(target)
drop_before = self.__get_drop_before(row_widget, x, y)
if not isinstance(item, CmbObject):
return
# TODO: handle dragging from one UI to another
if origin_item.ui_id != item.ui_id:
return
if origin_item.parent_id != item.parent_id:
if drop_before:
msg = _("Move {name} before {target}").format(name=origin_item.display_name, target=item.display_name)
else:
msg = _("Move {name} after {target}").format(name=origin_item.display_name, target=item.display_name)
self.__project.history_push(msg)
origin_item.parent_id = item.parent_id
else:
msg = None
parent = item.parent
origin_position = origin_item.position
target_position = item.position
if origin_position > target_position:
if drop_before:
position = item.position
else:
position = item.position + 1
else:
if drop_before:
position = item.position - 1
else:
position = item.position
parent.reorder_child(origin_item, position)
if msg:
self.__project.history_pop()
self.__project.set_selection([origin_item])
def __get_tree_expander(self, x, y):
pick = self.pick(x, y, Gtk.PickFlags.DEFAULT)
if pick is None:
return None
if isinstance(pick, Gtk.TreeExpander):
return pick
child = pick.get_first_child()
if child and isinstance(child, Gtk.TreeExpander):
return child
parent = pick.props.parent
if parent and isinstance(parent, Gtk.TreeExpander):
return parent
return None
def __on_activate(self, column_view, position):
item = self.__tree_model.get_item(position)
item.set_expanded(not item.get_expanded())
def do_query_tooltip(self, x, y, keyboard_mode, tooltip):
expander = self.__get_tree_expander(x, y)
if expander is None:
return False
obj = expander.get_item()
if isinstance(obj, CmbObject):
msg = obj.version_warning
if msg:
tooltip.set_text(msg)
return True
return False

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.1 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_context_menu.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->

View File

@ -24,7 +24,10 @@
#
import os
from gi.repository import GObject, Gio
from .cmb_path import CmbPath
from .cmb_objects_base import CmbBaseCSS
from cambalache import _
@ -34,6 +37,7 @@ class CmbCSS(CmbBaseCSS):
"file-changed": (GObject.SignalFlags.RUN_FIRST, None, ()),
}
path_parent = GObject.Property(type=CmbPath, flags=GObject.ParamFlags.READWRITE)
css = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
def __init__(self, **kwargs):
@ -53,9 +57,13 @@ class CmbCSS(CmbBaseCSS):
if pspec.name == "filename":
self.load_css()
@classmethod
def get_display_name(cls, css_id, filename):
return os.path.basename(filename) if filename else _("Unnamed CSS {css_id}").format(css_id=css_id)
@GObject.Property(type=str)
def display_name(self):
return self.filename if self.filename else _("Unnamed CSS {css_id}").format(css_id=self.css_id)
return CmbCSS.get_display_name(self.css_id, self.filename)
@GObject.Property(type=int)
def priority(self):

View File

@ -81,6 +81,7 @@ class CmbCSSEditor(Gtk.Grid):
self.set_sensitive(False)
return
self.filename.dirname = obj.project.dirname
self.set_sensitive(True)
for field, target in self.fields:
@ -107,10 +108,6 @@ class CmbCSSEditor(Gtk.Grid):
self.__update_provider_for()
self.__update_ui_button_label()
@Gtk.Template.Callback("on_remove_button_clicked")
def __on_remove_button_clicked(self, button):
self.emit("remove-css")
@Gtk.Template.Callback("on_save_button_clicked")
def __on_save_button_clicked(self, button):
self._object.save_css()

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.1 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_css_editor.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->
@ -39,11 +39,8 @@
</object>
</child>
<child>
<object class="CmbEntry" id="filename">
<property name="can-focus">True</property>
<object class="CmbFileButton" id="filename">
<property name="hexpand">True</property>
<property name="placeholder-text" translatable="yes">&lt;file name relative to project&gt;</property>
<property name="visible">True</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
@ -111,20 +108,6 @@
<child>
<object class="GtkBox">
<property name="spacing">4</property>
<child>
<object class="GtkButton" id="remove_button">
<property name="focusable">1</property>
<!-- <property name="tooltip-text" translatable="1">Remove CSS file from project</property> -->
<property name="halign">start</property>
<property name="valign">end</property>
<signal name="clicked" handler="on_remove_button_clicked"/>
<child>
<object class="GtkImage">
<property name="icon-name">app-remove-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">center</property>

View File

@ -70,6 +70,7 @@ class CmbDB(GObject.GObject):
"ui_library",
"css",
"css_ui",
"gresource",
"object",
"object_property",
"object_layout_property",
@ -81,6 +82,9 @@ class CmbDB(GObject.GObject):
self.__history_commands = {}
self.__table_column_mapping = {}
self._output_lowercase_boolean = False
self._output_use_enum_value = False
self.clipboard = []
self.clipboard_ids = []
@ -675,39 +679,24 @@ class CmbDB(GObject.GObject):
# Drop temp table
c.execute(f"DROP TABLE temp.{table};")
def load(self, filename):
# TODO: drop all data before loading?
if filename is None or not os.path.isfile(filename):
return
tree = etree.parse(filename)
root = tree.getroot()
target_tk = root.get("target_tk", None)
if target_tk != self.target_tk:
raise Exception(f"Can not load a {target_tk} target in {self.target_tk} project.")
version = self.__parse_version(root.get("version", None))
if version > self.version:
version = ".".join(map(str, version))
raise Exception(
f"File format {version} is not supported by this release,\nplease update to a newer version to open this file."
)
def load_old_format(self, root, version):
c = self.conn.cursor()
# Avoid circular dependencies errors
self.foreign_keys = False
self.ignore_check_constraints = True
# Support old format
all_tables = self.__tables + ["property", "signal"]
for child in root.getchildren():
if child.tag in all_tables:
self.__load_table_from_tuples(c, child.tag, child.text, version)
else:
raise Exception(f"Unknown tag {child.tag} in project file.")
self.foreign_keys = True
self.ignore_check_constraints = False
c.close()
def __load_accessibility_metadata(self, node):
@ -800,83 +789,6 @@ class CmbDB(GObject.GObject):
self.commit()
def save(self, filename):
def get_row(row):
r = None
for c in row:
if r:
r += ","
else:
r = ""
if type(c) is str:
# FIXME: find a better way to escape string
val = c.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n")
r += f'"{val}"'
elif c is None:
r += "None"
else:
r += str(c)
return f"\t({r})"
def _dump_query(c, query):
c.execute(query)
row = c.fetchone()
if row is None:
return None
retval = ""
while row is not None:
retval += get_row(row)
row = c.fetchone()
if row:
retval += ",\n"
return f"\n{retval}\n "
def append_data(project, name, data):
if data is None:
return
element = etree.Element(name)
element.text = data
project.append(element)
self.conn.commit()
c = self.conn.cursor()
project = E("cambalache-project", version=config.FILE_FORMAT_VERSION, target_tk=self.target_tk)
for table in self.__tables:
data = _dump_query(c, f"SELECT * FROM {table};")
append_data(project, table, data)
# DUMP custom properties and signals
for table in ["property", "signal"]:
data = _dump_query(
c, f"SELECT {table}.* FROM {table},type WHERE {table}.owner_id == type.type_id AND type.library_id IS NULL;"
)
append_data(project, table, data)
# Dump xml to file
with open(filename, "wb") as fd:
tree = etree.ElementTree(project)
tree.write(
fd,
pretty_print=True,
xml_declaration=True,
encoding="UTF-8",
standalone=False,
doctype='<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">',
)
fd.close()
c.close()
def move_to_fs(self, filename):
self.conn.commit()
@ -937,13 +849,99 @@ class CmbDB(GObject.GObject):
return ui_id
def add_gresource(
self,
resource_type,
parent_id=None,
gresources_filename=None,
gresource_prefix=None,
file_filename=None,
file_compressed=None,
file_preprocess=None,
file_alias=None
):
if resource_type not in ["gresources", "gresource", "file"]:
return
c = self.conn.cursor()
if resource_type == "gresources":
c.execute("SELECT count(gresource_id) FROM gresource WHERE parent_id IS NULL;")
else:
c.execute("SELECT count(gresource_id) FROM gresource WHERE parent_id=?;", (parent_id, ))
position = c.fetchone()[0]
c.execute(
"""
INSERT INTO gresource (
resource_type,
parent_id,
position,
gresources_filename,
gresource_prefix,
file_filename,
file_compressed,
file_preprocess,
file_alias
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
""",
(
resource_type,
parent_id,
position,
gresources_filename,
gresource_prefix,
file_filename,
file_compressed,
file_preprocess,
file_alias
)
)
gresource_id = c.lastrowid
c.close()
return gresource_id
def __add_internal_child(self, ui_id, object_id, child):
# Do not automatically create internal child that depend on a property
if child.creation_property_id:
row = self.execute(
"SELECT value FROM object_property WHERE ui_id=? AND object_id=? AND property_id=?;",
(ui_id, object_id, child.creation_property_id)
).fetchone()
if not row:
row = self.execute(
"SELECT default_value FROM property WHERE owner_id=? AND property_id=?;",
(child.type_id, child.creation_property_id)
).fetchone()
should_create = utils.bool_from_string(row[0]) if row else False
if not should_create:
return
child_id = self.execute("SELECT coalesce(MAX(object_id), 0) + 1 FROM object WHERE ui_id=?;", (ui_id,)).fetchone()[0]
position = self.execute(
"SELECT coalesce(MAX(position), -1) + 1 FROM object WHERE ui_id=? AND parent_id=?;",
(ui_id, object_id)
).fetchone()[0]
self.execute(
"INSERT INTO object (ui_id, object_id, type_id, parent_id, internal, position) VALUES (?, ?, ?, ?, ?, ?);",
(ui_id, child_id, child.internal_type, object_id, child.internal_child_id, position),
)
for internal_child_id, internal_child in child.children.items():
self.__add_internal_child(ui_id, child_id, internal_child)
def add_object(
self,
ui_id,
obj_type,
name=None,
parent_id=None,
internal_child=None,
internal=None,
child_type=None,
comment=None,
layout=None,
@ -982,9 +980,14 @@ class CmbDB(GObject.GObject):
INSERT INTO object (ui_id, object_id, type_id, name, parent_id, internal, type, comment, position)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
""",
(ui_id, object_id, obj_type, name, parent_id, internal_child, child_type, comment, position),
(ui_id, object_id, obj_type, name, parent_id, internal, child_type, comment, position),
)
# Automatically add internal children
info = self.type_info.get(obj_type, None)
for internal_child_id, child in info.internal_children.items():
self.__add_internal_child(ui_id, object_id, child)
# Get parent type for later
if layout or inline_property:
c.execute("SELECT type_id FROM object WHERE ui_id=? AND object_id=?;", (ui_id, parent_id))
@ -1056,41 +1059,12 @@ class CmbDB(GObject.GObject):
self.__collect_error("unknown-tag", node, f"{owner}:{name}" if owner is not None and name else name)
def __node_get(self, node, *args, collect_errors=True):
keys = node.keys()
knowns = []
retval = []
errors = [] if collect_errors else None
retval = utils.xml_node_get(node, *args, errors=errors)
def get_key_val(node, attr):
tokens = attr.split(":")
key = tokens[0]
val = node.get(key, None)
if len(tokens) > 1:
t = tokens[1]
if t == "bool":
return (key, val.lower() in {"1", "t", "y", "true", "yes"} if val else False)
elif t == "int":
return (key, int(val))
return (key, val)
for attr in args:
if isinstance(attr, list):
for opt in attr:
key, val = get_key_val(node, opt)
retval.append(val)
knowns.append(key)
elif attr in keys:
key, val = get_key_val(node, attr)
retval.append(val)
knowns.append(key)
elif collect_errors:
self.__collect_error("missing-attr", node, attr)
if collect_errors:
unknown = list(set(keys) - set(knowns))
for attr in unknown:
self.__collect_error("unknown-attr", node, attr)
if errors:
for error, node, attr in errors:
self.__collect_error(error, node, attr)
return retval
@ -1137,9 +1111,16 @@ class CmbDB(GObject.GObject):
property_id = name.replace("_", "-")
pinfo = self.__get_property_info(info, property_id)
if pinfo is None:
self.__collect_error("unknown-property", prop, f"{info.type_id}:{property_id}")
return
# Property value
value = prop.text
if pinfo.is_object and value:
value = value.strip()
# Initialize to null
inline_object_id = None
@ -1193,11 +1174,6 @@ class CmbDB(GObject.GObject):
):
comment = self.__node_get_comment(prop)
# Insert property
if not pinfo:
self.__collect_error("unknown-property", prop, f"{info.type_id}:{property_id}")
return
# Need to remap object ids on paste
if object_id_map and pinfo.is_object:
value = object_id_map.get(value, value)
@ -1339,6 +1315,11 @@ class CmbDB(GObject.GObject):
if object_id_map and user_data:
user_data = object_id_map.get(user_data, user_data)
# If object/user_data is set then swapped is by default on
if user_data and signal.get("swapped", None) is None:
# Force swapped to true when there is an object
swap = True
# Insert signal
if not owner_id:
self.__collect_error("unknown-signal", signal, f"{info.type_id}:{signal_id}")
@ -1417,19 +1398,6 @@ class CmbDB(GObject.GObject):
property_id = name.replace("_", "-")
comment = self.__node_get_comment(prop)
owner_id = self.__get_layout_property_owner(parent_type[0], property_id)
owner_info = self.type_info.get(owner_id, None)
if owner_info:
pinfo = owner_info.properties.get(property_id, None)
# Update object position if this layout property is_position
if pinfo and pinfo.is_position:
try:
c.execute("UPDATE object SET position=? WHERE ui_id=? AND object_id=?;", (prop.text, ui_id, object_id))
except Exception:
# Ignore duplicated positions
pass
continue
try:
c.execute(
@ -1925,17 +1893,13 @@ class CmbDB(GObject.GObject):
(ui_id,),
)
def import_file(self, filename, projectdir="."):
def import_from_node(self, root, relpath):
custom_fragments = []
self.foreign_keys = False
# Clear parsing errors
self.errors = {}
tree = etree.parse(filename)
root = tree.getroot()
if root.tag != "interface":
raise Exception(_("Unknown root tag {tag}").format(tag=root.tag))
@ -1944,7 +1908,7 @@ class CmbDB(GObject.GObject):
target_tk = self.target_tk
lib, ver, inferred = CmbDB._get_target_from_node(root)
if lib is not None and (target_tk == "gtk-4.0" and lib != "gtk") or (target_tk == "gtk+-3.0" and lib != "gtk+"):
if lib is not None and ((target_tk == "gtk-4.0" and lib != "gtk") or (target_tk == "gtk+-3.0" and lib != "gtk+")):
# Translators: This text will be used in the next two string as {convert}
convert = _("\nUse gtk4-builder-tool first to convert file.") if target_tk == "gtk-4.0" else ""
@ -1973,8 +1937,7 @@ class CmbDB(GObject.GObject):
# Make sure there is no attributes in root tag other than domain
domain, = self.__node_get(root, ["domain"])
basename = os.path.basename(filename)
relpath = os.path.relpath(filename, projectdir)
basename = os.path.basename(relpath) if relpath else None
ui_id = self.add_ui(basename, relpath, requirements, domain, comment)
# These values come from Glade
@ -2010,7 +1973,7 @@ class CmbDB(GObject.GObject):
elif child.tag == "requires":
pass
elif child.tag is etree.Comment:
comment = etree.tostring(child).decode("utf-8").strip()
comment = child.text.strip()
comment = comment.removeprefix("<!--").removesuffix("-->").strip()
# Import interface data from Glade comments
@ -2038,7 +2001,7 @@ class CmbDB(GObject.GObject):
c.execute("UPDATE ui SET custom_fragment=? WHERE ui_id=?", (fragment, ui_id))
# Check for parsing errors and append .cmb if something is not supported
if len(self.errors):
if relpath and len(self.errors):
filename, etx = os.path.splitext(relpath)
c.execute("UPDATE ui SET filename=? WHERE ui_id=?", (f"{filename}.cmb.ui", ui_id))
@ -2049,14 +2012,49 @@ class CmbDB(GObject.GObject):
return ui_id
def import_gresource_from_node(self, root, relpath):
# Clear parsing errors
self.errors = {}
if root.tag != "gresources":
raise Exception(_("Unknown root tag {tag}").format(tag=root.tag))
gresource_id = self.add_gresource("gresources", gresources_filename=relpath)
for child in root.iterchildren():
if child.tag != "gresource":
self.__unknown_tag(child, root, child.tag)
continue
prefix, = self.__node_get(child, ["prefix"])
resource_id = self.add_gresource("gresource", parent_id=gresource_id, gresource_prefix=prefix)
for file in child.iterchildren():
if file.tag != "file":
self.__unknown_tag(file, child, file.tag)
continue
compressed, preprocess, alias = self.__node_get(
file,
["compressed:bool", "preprocess", "alias"],
collect_errors=False
)
self.add_gresource(
"file",
parent_id=resource_id,
file_filename=file.text,
file_compressed=compressed,
file_preprocess=preprocess,
file_alias=alias
)
return gresource_id
def __node_add_comment(self, node, comment):
if comment:
node.addprevious(etree.Comment(comment))
def __node_set(self, node, attr, val):
if val is not None:
node.set(attr, str(val))
def __export_menu(self, ui_id, object_id, merengue=False, ignore_id=False):
c = self.conn.cursor()
@ -2065,13 +2063,13 @@ class CmbDB(GObject.GObject):
if type_id == GMENU_TYPE:
obj = E.menu()
self.__node_set(obj, "id", f"__cmb__{ui_id}.{object_id}" if merengue else name)
utils.xml_node_set(obj, "id", f"__cmb__{ui_id}.{object_id}" if merengue else name)
elif type_id == GMENU_SECTION_TYPE:
obj = E.section()
self.__node_set(obj, "id", f"__cmb__{ui_id}.{object_id}" if merengue else name)
utils.xml_node_set(obj, "id", f"__cmb__{ui_id}.{object_id}" if merengue else name)
elif type_id == GMENU_SUBMENU_TYPE:
obj = E.submenu()
self.__node_set(obj, "id", f"__cmb__{ui_id}.{object_id}" if merengue else name)
utils.xml_node_set(obj, "id", f"__cmb__{ui_id}.{object_id}" if merengue else name)
elif type_id == GMENU_ITEM_TYPE:
obj = E.item()
else:
@ -2101,9 +2099,9 @@ class CmbDB(GObject.GObject):
node.text = value
if translatable:
self.__node_set(node, "translatable", "yes")
self.__node_set(node, "context", translation_context)
self.__node_set(node, "comments", translation_comments)
utils.xml_node_set(node, "translatable", "yes")
utils.xml_node_set(node, "context", translation_context)
utils.xml_node_set(node, "comments", translation_comments)
obj.append(node)
@ -2201,9 +2199,9 @@ class CmbDB(GObject.GObject):
ntag.set(key, value)
if translatable:
self.__node_set(ntag, "translatable", "yes")
self.__node_set(ntag, "context", translation_context)
self.__node_set(ntag, "comments", translation_comments)
utils.xml_node_set(ntag, "translatable", "yes")
utils.xml_node_set(ntag, "context", translation_context)
utils.xml_node_set(ntag, "comments", translation_comments)
for tag in info.children:
self.__export_object_data(ui_id, object_id, owner_id, tag, info.children[tag], ntag, id)
@ -2232,19 +2230,53 @@ class CmbDB(GObject.GObject):
for child in taginfo.children:
self.__export_object_data(ui_id, object_id, owner_id, child, taginfo.children[child], ntag, id, merengue=merengue)
def __internal_object_is_empty(self, ui_id, object_id):
# Check if internal object is empty or not, it has name, xml fragments, children, property or any other data
row = self.execute(
"""
WITH RECURSIVE d(ui_id, object_id)
AS (
VALUES(?, ?)
UNION
SELECT o.ui_id, o.object_id FROM object AS o, d WHERE o.ui_id=d.ui_id AND o.parent_id=d.object_id
)
SELECT
(SELECT COUNT(ui_id) FROM object
WHERE (ui_id, object_id) IN d AND
(name IS NOT NULL OR type IS NOT NULL OR custom_fragment IS NOT NULL OR custom_child_fragment IS NOT NULL)
),
(SELECT COUNT(ui_id) FROM object WHERE internal IS NULL AND (ui_id, parent_id) IN d),
(SELECT COUNT(ui_id) FROM object_property WHERE (ui_id, object_id) IN d),
(SELECT COUNT(ui_id) FROM object_layout_property WHERE (ui_id, child_id) IN d),
(SELECT COUNT(ui_id) FROM object_signal WHERE (ui_id, object_id) IN d),
(SELECT COUNT(ui_id) FROM object_data WHERE (ui_id, object_id) IN d);
""",
(ui_id, object_id)
).fetchone()
n_objs, n_children, n_props, n_layout_props, n_signals, n_data = row
return n_objs == 0 and n_children == 0 and n_props == 0 and n_layout_props == 0 and n_signals == 0 and n_data == 0
def __export_object(self, ui_id, object_id, merengue=False, template_id=None, ignore_id=False):
c = self.conn.cursor()
cc = self.conn.cursor()
c.execute("SELECT type_id, name, custom_fragment FROM object WHERE ui_id=? AND object_id=?;", (ui_id, object_id))
type_id, name, custom_fragment = c.fetchone()
# Special case <menu>
if type_id == GMENU_TYPE:
c.close()
return self.__export_menu(ui_id, object_id, merengue=merengue, ignore_id=ignore_id)
info = self.type_info.get(type_id, None)
if info is None:
logger.warning(f"Type info missing for type {type_id}")
c.close()
return None
cc = self.conn.cursor()
merengue_template = merengue and info.library_id is None and info.parent_id is not None
# Check if this is a custom template object
# We do not export object templates in merengue mode, this way we do not really need to instantiate a real type
@ -2266,28 +2298,28 @@ class CmbDB(GObject.GObject):
# Set object id
if not ignore_id:
self.__node_set(obj, "id", f"__cmb__{ui_id}.{object_id}")
utils.xml_node_set(obj, "id", f"__cmb__{ui_id}.{object_id}")
elif not merengue and template_id == object_id:
obj = E.template()
self.__node_set(obj, "class", name)
self.__node_set(obj, "parent", type_id)
utils.xml_node_set(obj, "class", name)
utils.xml_node_set(obj, "parent", type_id)
else:
obj = E.object()
if merengue:
workspace_type = info.workspace_type
self.__node_set(obj, "class", workspace_type if workspace_type else type_id)
utils.xml_node_set(obj, "class", workspace_type if workspace_type else type_id)
if merengue_template:
# From now own all output should be without an ID
# because we do not want so select internal widget from the template
ignore_id = True
elif not ignore_id:
self.__node_set(obj, "id", f"__cmb__{ui_id}.{object_id}")
utils.xml_node_set(obj, "id", f"__cmb__{ui_id}.{object_id}")
else:
self.__node_set(obj, "class", type_id)
utils.xml_node_set(obj, "class", type_id)
if not ignore_id:
self.__node_set(obj, "id", name)
utils.xml_node_set(obj, "id", name)
# Create class hierarchy list
hierarchy = [type_id] + info.hierarchy if info else [type_id]
@ -2343,6 +2375,7 @@ class CmbDB(GObject.GObject):
value = None
value_node = None
pinfo = self.type_info.get(property_type_id, None)
is_inline_object = not disable_inline_object and self.target_tk == "gtk-4.0"
@ -2370,6 +2403,10 @@ class CmbDB(GObject.GObject):
value = obj_name
elif property_type_id == "GBytes":
value = etree.CDATA(val)
elif self._output_lowercase_boolean and property_type_id == "gboolean":
value = "true" if utils.bool_from_string(val) else "false"
elif self._output_use_enum_value and pinfo and pinfo.parent_id == "enum":
value = str(pinfo.enum_get_value_as_string(val, use_nick=False))
else:
value = val
@ -2380,17 +2417,17 @@ class CmbDB(GObject.GObject):
node.append(value_node)
if translatable:
self.__node_set(node, "translatable", "yes")
self.__node_set(node, "context", translation_context)
self.__node_set(node, "comments", translation_comments)
utils.xml_node_set(node, "translatable", "yes")
utils.xml_node_set(node, "context", translation_context)
utils.xml_node_set(node, "comments", translation_comments)
if bind_source_id and bind_owner_id and bind_property_id:
bind_source = self.__get_object_name(ui_id, bind_source_id, merengue=merengue)
if bind_source:
self.__node_set(node, "bind-source", bind_source)
self.__node_set(node, "bind-property", bind_property_id)
self.__node_set(node, "bind-flags", bind_flags)
utils.xml_node_set(node, "bind-source", bind_source)
utils.xml_node_set(node, "bind-property", bind_property_id)
utils.xml_node_set(node, "bind-flags", bind_flags)
obj.append(node)
self.__node_add_comment(node, comment)
@ -2411,13 +2448,21 @@ class CmbDB(GObject.GObject):
),
):
signal_id, handler, detail, data, swap, after, comment = row
name = f"{signal_id}::{detail}" if detail is not None else signal_id
node = E.signal(name=name, handler=handler)
self.__node_set(node, "object", data)
if swap:
self.__node_set(node, "swapped", "yes")
if data:
# if object is set, swap defaults to True
if not swap:
utils.xml_node_set(node, "swapped", "False")
utils.xml_node_set(node, "object", data)
elif swap:
utils.xml_node_set(node, "swapped", "True")
if after:
self.__node_set(node, "after", "yes")
utils.xml_node_set(node, "after", "True")
obj.append(node)
self.__node_add_comment(node, comment)
@ -2481,6 +2526,7 @@ class CmbDB(GObject.GObject):
owner_id,
) = row
pinfo = self.type_info.get(property_type_id, None)
value = None
# Ignore properties depending on metadata (Gtk4)
@ -2500,6 +2546,15 @@ class CmbDB(GObject.GObject):
if obj_name is None:
continue
value = obj_name
elif self._output_lowercase_boolean and property_type_id == "gboolean":
value = "true" if utils.bool_from_string(val) else "false"
elif self._output_lowercase_boolean and property_type_id == "CmbBooleanUndefined":
if val == "undefined":
value = "undefined"
else:
value = "true" if utils.bool_from_string(val) else "false"
elif self._output_use_enum_value and pinfo and pinfo.parent_id == "enum":
value = str(pinfo.enum_get_value_as_string(val, use_nick=False))
else:
value = val
@ -2553,9 +2608,9 @@ class CmbDB(GObject.GObject):
node.text = value
if translatable:
self.__node_set(node, "translatable", "yes")
self.__node_set(node, "context", translation_context)
self.__node_set(node, "comments", translation_comments)
utils.xml_node_set(node, "translatable", "yes")
utils.xml_node_set(node, "context", translation_context)
utils.xml_node_set(node, "comments", translation_comments)
self.__node_add_comment(node, comment)
@ -2586,9 +2641,18 @@ class CmbDB(GObject.GObject):
child_position = 0
# FIXME: only export placeholders for GtkBox
# This needs to be removed and handled directly in merengue by passing postion together with idi
is_box = info.is_a("GtkBox")
# FIXME: only export placeholders for GtkBox and box like containers
# This needs to be removed and handled directly in merengue by passing position together with id
is_box = info.is_a("GtkBox") or type_id in [
"GtkHeaderBar",
"GtkNotebook",
"GtkActionBar",
"GtkPopoverMenuBar",
"GtkStack",
"GtkToolItemGroup",
"GtkPopoverMenu",
"HdyHeaderBar"
]
# Children
for row in c.execute(
@ -2604,7 +2668,24 @@ class CmbDB(GObject.GObject):
):
child_id, internal, ctype, comment, position, custom_child_fragment = row
# Here we try to output internal children only if nescesary
if not merengue and internal and self.__internal_object_is_empty(ui_id, child_id):
continue
if merengue and is_box:
# FIXME: On Gtk 3 we get the position from the layout property
if self.target_tk == "gtk+-3.0":
r = cc.execute(
"""
SELECT value
FROM object_layout_property
WHERE ui_id=? AND object_id=? AND child_id=? AND owner_id=? AND property_id='position'
""",
(ui_id, object_id, child_id, layout_class)
).fetchone()
if r:
position = int(r[0]) if r[0] else 0
position = position if position is not None else 0
while child_position < position:
@ -2617,8 +2698,8 @@ class CmbDB(GObject.GObject):
child_obj = self.__export_object(ui_id, child_id, merengue=merengue, ignore_id=ignore_id)
child = E.child(child_obj)
self.__node_set(child, "internal-child", internal)
self.__node_set(child, "type", ctype)
utils.xml_node_set(child, "internal-child", internal)
utils.xml_node_set(child, "type", ctype)
self.__node_add_comment(child_obj, comment)
obj.append(child)
@ -2695,8 +2776,10 @@ class CmbDB(GObject.GObject):
translation_domain, comment, template_id, custom_fragment = row
node = E.interface()
node.addprevious(etree.Comment(f" Created with Cambalache {config.VERSION} "))
self.__node_set(node, "domain", translation_domain)
utils.xml_node_set(node, "domain", translation_domain)
self.__node_add_comment(node, comment)
@ -2769,6 +2852,9 @@ class CmbDB(GObject.GObject):
):
object_id, comment = row
child = self.__export_object(ui_id, object_id, merengue=merengue, template_id=template_id)
if child is None:
continue
node.append(child)
self.__node_add_comment(child, comment)
@ -2787,6 +2873,72 @@ class CmbDB(GObject.GObject):
return etree.tostring(ui, pretty_print=True, xml_declaration=True, encoding="UTF-8").decode("UTF-8")
def export_gresource(self, gresources_id, skip_version_comment=False):
c = self.conn.cursor()
c.execute(
"SELECT gresources_filename FROM gresource WHERE resource_type='gresources' AND gresource_id=?;",
(gresources_id, )
)
row = c.fetchone()
if row is None:
c.close()
return None
root = E.gresources()
if not skip_version_comment:
root.addprevious(etree.Comment(f" Created with Cambalache {config.VERSION} "))
cc = self.conn.cursor()
# Iterate over resources
for row in c.execute(
"SELECT gresource_id, gresource_prefix FROM gresource WHERE resource_type='gresource' AND parent_id=?;",
(gresources_id, )
):
resource_id, resource_prefix = row
gresource = E.gresource()
utils.xml_node_set(gresource, "prefix", resource_prefix)
for row in cc.execute(
"""
SELECT file_filename, file_compressed, file_preprocess, file_alias
FROM gresource
WHERE resource_type='file' AND parent_id=?;
""",
(resource_id, )
):
filename, compressed, preprocess, alias = row
file = E.file()
if filename:
file.text = filename
if compressed:
utils.xml_node_set(file, "compressed", "true")
utils.xml_node_set(file, "preprocess", preprocess)
utils.xml_node_set(file, "alias", alias)
gresource.append(file)
root.append(gresource)
c.close()
cc.close()
return etree.ElementTree(root)
def gresource_tostring(self, gresource_id):
gresource = self.export_gresource(gresource_id)
if gresource is None:
return None
return etree.tostring(gresource, pretty_print=True, xml_declaration=True, encoding="UTF-8").decode("UTF-8")
def clipboard_copy(self, selection):
self.clipboard = []
self.clipboard_ids = []
@ -2899,6 +3051,20 @@ class CmbDB(GObject.GObject):
(ui_id, ) if parent_id is None else (ui_id, parent_id)
)
def update_gresource_children_position(self, gresource_id):
self.execute(
"""
UPDATE gresource SET position=new.position - 1
FROM (
SELECT row_number() OVER (PARTITION BY parent_id ORDER BY position) position, parent_id, gresource_id
FROM gresource
WHERE parent_id=?
) AS new
WHERE gresource.parent_id=new.parent_id AND gresource.gresource_id=new.gresource_id;
""",
(gresource_id, )
)
# Function used in SQLite

View File

@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.1 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_db_inspector.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->
<requires lib="gtk" version="4.0"/>
<template class="CmbDBInspector" parent="GtkBox">

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.1 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_fragment_editor.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->

151
cambalache/cmb_gresource.py Normal file
View File

@ -0,0 +1,151 @@
#
# Cambalache GResource wrapper
#
# Copyright (C) 2024 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from gi.repository import GObject, Gio
from .cmb_path import CmbPath
from .cmb_objects_base import CmbBaseGResource
from .cmb_list_error import CmbListError
from cambalache import _
class CmbGResource(CmbBaseGResource, Gio.ListModel):
path_parent = GObject.Property(type=CmbPath, flags=GObject.ParamFlags.READWRITE)
def __init__(self, **kwargs):
self._last_known = None
super().__init__(**kwargs)
self.connect("notify", self.__on_notify)
def __bool__(self):
return True
def __str__(self):
return f"CmbGResource<{self.resource_type}> id={self.gresource_id}"
def __on_notify(self, obj, pspec):
resource_type = self.resource_type
if (resource_type == "gresources" and pspec.name == "gresources-filename") or \
(resource_type == "gresource" and pspec.name == "gresource-prefix") or \
(resource_type == "file" and pspec.name == "file-filename"):
obj.notify("display-name")
self.project._gresource_changed(self, pspec.name)
@GObject.Property(type=CmbBaseGResource)
def parent(self):
if self.resource_type in ["gresource", "file"]:
return self.project.get_gresource_by_id(self.parent_id)
return None
@GObject.Property(type=CmbBaseGResource)
def gresources_bundle(self):
resource_type = self.resource_type
if resource_type == "gresource":
return self.parent
elif resource_type == "file":
return self.parent.parent
return self
@GObject.Property(type=str)
def display_name(self):
resource_type = self.resource_type
if resource_type == "gresources":
gresources_filename = self.gresources_filename
if gresources_filename:
basename, relpath = self.project._get_basename_relpath(self.gresources_filename)
return basename
return _("Unnamed GResource {id}").format(id=self.gresource_id)
elif resource_type == "gresource":
gresource_prefix = self.gresource_prefix
return gresource_prefix if gresource_prefix else _("Unprefixed resource {id}").format(id=self.gresource_id)
elif resource_type == "file":
file_filename = self.file_filename
return file_filename if file_filename else _("Unnamed file {id}").format(id=self.gresource_id)
# GListModel helpers
def _save_last_known_parent_and_position(self):
self._last_known = (self.parent, self.position)
def _update_new_parent(self):
parent = self.parent
position = self.position
# Emit GListModel signal to update model
if parent:
parent.items_changed(position, 0, 1)
parent.notify("n-items")
self._last_known = None
def _remove_from_old_parent(self):
if self._last_known is None:
return
parent, position = self._last_known
# Emit GListModel signal to update model
if parent:
parent.items_changed(position, 1, 0)
parent.notify("n-items")
self._last_known = None
# GListModel iface
def do_get_item(self, position):
gresource_id = self.gresource_id
key = self.db_get(
"SELECT gresource_id FROM gresource WHERE parent_id=? AND position=?;",
(gresource_id, position)
)
if key is not None:
return self.project.get_gresource_by_id(key)
# This should not happen
return CmbListError()
def do_get_item_type(self):
return CmbBaseGResource
@GObject.Property(type=int)
def n_items(self):
if self.resource_type in ["gresources", "gresource"]:
retval = self.db_get("SELECT COUNT(gresource_id) FROM gresource WHERE parent_id=?;", (self.gresource_id, ))
return retval if retval is not None else 0
else:
return 0
def do_get_n_items(self):
return self.n_items

View File

@ -0,0 +1,117 @@
#
# CmbGResourceEditor - Cambalache GResource Editor
#
# Copyright (C) 2024 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from gi.repository import GObject, Gtk
from .cmb_gresource import CmbGResource
from cambalache import getLogger
logger = getLogger(__name__)
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_gresource_editor.ui")
class CmbGResourceEditor(Gtk.Box):
__gtype_name__ = "CmbGResourceEditor"
stack = Gtk.Template.Child()
gresources_filename = Gtk.Template.Child()
gresource_prefix = Gtk.Template.Child()
file_filename = Gtk.Template.Child()
file_compressed = Gtk.Template.Child()
file_preprocess = Gtk.Template.Child()
file_alias = Gtk.Template.Child()
fields = [
("gresources", "gresources_filename", "cmb-value"),
("gresource", "gresource_prefix", "cmb-value"),
("file", "file_filename", "cmb-value"),
("file", "file_compressed", "active"),
("file", "file_preprocess", "cmb-value"),
("file", "file_alias", "cmb-value"),
]
def __init__(self, **kwargs):
self._object = None
self._bindings = []
super().__init__(**kwargs)
@GObject.Property(type=CmbGResource)
def object(self):
return self._object
@object.setter
def _set_object(self, obj):
if obj == self._object:
return
for binding in self._bindings:
binding.unbind()
self._bindings = []
self._object = obj
if obj is None:
self.set_sensitive(False)
for for_type, field, target in self.fields:
widget = getattr(self, field)
target_prop = getattr(widget, target)
if isinstance(target_prop, int):
setattr(widget, target, 0)
else:
setattr(widget, target, None)
return
resource_type = obj.resource_type
self.set_sensitive(True)
self.stack.set_visible_child_name(resource_type)
self.gresources_filename.dirname = obj.project.dirname
self.file_filename.dirname = obj.project.dirname
for for_type, field, target in self.fields:
if resource_type != for_type:
continue
binding = GObject.Object.bind_property(
obj,
field,
getattr(self, field),
target,
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
)
self._bindings.append(binding)
@Gtk.Template.Callback("on_add_gresource_button_clicked")
def __on_add_gresource_button_clicked(self, button):
self._object.project.add_gresource("gresource", parent_id=self._object.gresource_id)
@Gtk.Template.Callback("on_add_file_button_clicked")
def __on_add_file_button_clicked(self, button):
self._object.project.add_gresource("file", parent_id=self._object.gresource_id)
Gtk.WidgetClass.set_css_name(CmbGResourceEditor, "CmbGResourceEditor")

View File

@ -0,0 +1,241 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_gresource_editor.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->
<requires lib="gtk" version="4.12"/>
<template class="CmbGResourceEditor" parent="GtkBox">
<child>
<object class="GtkStack" id="stack">
<property name="transition-type">crossfade</property>
<child>
<object class="GtkStackPage">
<property name="child">
<object class="GtkGrid">
<property name="column-spacing">4</property>
<property name="row-spacing">4</property>
<child>
<object class="GtkLabel">
<property name="halign">end</property>
<property name="label" translatable="yes">Filename</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="CmbFileButton" id="gresources_filename">
<property name="hexpand">True</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">end</property>
<property name="label" translatable="yes">Add</property>
<layout>
<property name="column">0</property>
<property name="column-span">1</property>
<property name="row">1</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">* This resource file need to be compiled and loaded at runtime</property>
<property name="valign">end</property>
<property name="vexpand">True</property>
<property name="wrap">True</property>
<property name="xalign">0.0</property>
<layout>
<property name="column">0</property>
<property name="column-span">2</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkButton" id="add_gresource_button">
<property name="halign">start</property>
<property name="label">GResource</property>
<signal name="clicked" handler="on_add_gresource_button_clicked"/>
<layout>
<property name="column">1</property>
<property name="column-span">1</property>
<property name="row">1</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
</object>
</property>
<property name="name">gresources</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="child">
<object class="GtkGrid">
<property name="column-spacing">4</property>
<property name="row-spacing">4</property>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Prefix</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="CmbEntry" id="gresource_prefix">
<property name="hexpand">True</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">* Files defined inside this will be available at gresource://prefix</property>
<property name="valign">end</property>
<property name="vexpand">True</property>
<property name="wrap">True</property>
<property name="xalign">0.0</property>
<layout>
<property name="column">0</property>
<property name="column-span">2</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Add</property>
<layout>
<property name="column">0</property>
<property name="column-span">1</property>
<property name="row">1</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkButton" id="add_file_button">
<property name="halign">start</property>
<property name="label" translatable="yes">file</property>
<signal name="clicked" handler="on_add_file_button_clicked"/>
<layout>
<property name="column">1</property>
<property name="column-span">1</property>
<property name="row">1</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
</object>
</property>
<property name="name">gresource</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="child">
<object class="GtkGrid">
<property name="column-spacing">4</property>
<property name="row-spacing">4</property>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Filename</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Compressed</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Preprocess</property>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Alias</property>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkSwitch" id="file_compressed">
<property name="halign">start</property>
<property name="hexpand">True</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="CmbEntry" id="file_preprocess">
<property name="hexpand">True</property>
<layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="CmbEntry" id="file_alias">
<property name="hexpand">True</property>
<layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="CmbFileButton" id="file_filename">
<property name="use-open">True</property>
<layout>
<property name="column">1</property>
<property name="column-span">1</property>
<property name="row">0</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
</object>
</property>
<property name="name">file</property>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -35,9 +35,7 @@ class CmbLayoutProperty(CmbBaseLayoutProperty):
info = GObject.Property(type=CmbPropertyInfo, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
def __init__(self, **kwargs):
self.__on_init = True
super().__init__(**kwargs)
self.__on_init = False
self.version_warning = None
owner_info = self.project.type_info.get(self.info.owner_id, None)
@ -68,11 +66,6 @@ class CmbLayoutProperty(CmbBaseLayoutProperty):
@value.setter
def _set_value(self, value):
# Update object position if this is a position property
if self.info.is_position and not self.__on_init:
self.object.parent.reorder_child(self.object, int(value) if value else 0)
return
c = self.project.db.cursor()
if value is None or value == self.info.default_value:
@ -113,9 +106,6 @@ class CmbLayoutProperty(CmbBaseLayoutProperty):
(self.ui_id, self.object_id, self.child_id, self.owner_id, self.property_id, value),
)
if not self.__on_init:
self.object._layout_property_changed(self)
c.close()
def _update_version_warning(self):

View File

@ -36,3 +36,7 @@ class CmbListError(CmbBase):
def display_name(self):
return "list error"
@GObject.Property(type=int)
def n_items(self):
return 0

260
cambalache/cmb_list_view.py Normal file
View File

@ -0,0 +1,260 @@
#
# CmbColumnView
#
# Copyright (C) 2024 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from gi.repository import GLib, GObject, Gdk, Gtk
from .cmb_ui import CmbUI
from .cmb_object import CmbObject
from .cmb_gresource import CmbGResource
from .cmb_context_menu import CmbContextMenu
from .cmb_path import CmbPath
from .cmb_project import CmbProject
from .cmb_tree_expander import CmbTreeExpander
class CmbListView(Gtk.ListView):
__gtype_name__ = "CmbListView"
def __init__(self, **kwargs):
self.__project = None
self.__tree_model = None
self.__in_selection_change = False
self.single_selection = Gtk.SingleSelection()
super().__init__(**kwargs)
self.props.has_tooltip = True
self.props.hexpand = True
factory = Gtk.SignalListItemFactory()
factory.connect("setup", self._on_factory_setup)
factory.connect("bind", self._on_factory_bind)
factory.connect("unbind", self._on_factory_unbind)
self.props.factory = factory
self.single_selection.connect("notify::selected-item", self.__on_selected_item_notify)
self.set_model(self.single_selection)
gesture = Gtk.GestureClick(button=Gdk.BUTTON_SECONDARY)
gesture.connect("pressed", self.__on_button_press)
self.add_controller(gesture)
self.connect("activate", self.__on_activate)
self.add_css_class("navigation-sidebar")
self.add_css_class("cmb-list-view")
def __on_button_press(self, gesture, npress, x, y):
expander = self.__get_tree_expander(x, y)
if expander is None or npress != 1:
return False
# Select row at x,y
list_row = expander.get_list_row()
self.single_selection.set_selected(list_row.get_position())
menu = CmbContextMenu()
if self.__project:
menu.target_tk = self.__project.target_tk
menu.set_parent(self)
menu.popup_at(x, y)
return True
def __get_path_parent(self, obj):
if isinstance(obj, CmbObject):
parent = obj.parent
return parent if parent else obj.ui
elif isinstance(obj, CmbGResource):
return obj.path_parent if obj.resource_type == "gresources" else obj.parent
elif isinstance(obj, CmbProject):
return None
return obj.path_parent
def __get_object_ancestors(self, obj):
ancestors = set()
parent = self.__get_path_parent(obj)
while parent:
ancestors.add(parent)
parent = self.__get_path_parent(parent)
return ancestors
def __object_ancestor_expand(self, obj):
ancestors = self.__get_object_ancestors(obj)
i = 0
# Iterate over tree model
# NOTE: only visible/expanded rows are returned
list_row = self.__tree_model.get_row(i)
while list_row:
item = list_row.get_item()
# Return position if we reached the object row
if item == obj:
return i
elif item in ancestors:
# Expand row if its part of the hierarchy
list_row.set_expanded(True)
i += 1
list_row = self.__tree_model.get_row(i)
return None
def __on_project_selection_changed(self, p):
list_row = self.single_selection.get_selected_item()
current_selection = [list_row.get_item()] if list_row else []
selection = self.__project.get_selection()
if selection == current_selection:
return
self.__in_selection_change = True
if len(selection) > 0:
position = self.__object_ancestor_expand(selection[0])
if position is not None:
self.single_selection.select_item(position, True)
else:
self.single_selection.unselect_all()
else:
self.single_selection.unselect_all()
self.__in_selection_change = False
@GObject.Property(type=CmbProject)
def project(self):
return self.__project
@project.setter
def _set_project(self, project):
if self.__project:
self.__project.disconnect_by_func(self.__on_project_selection_changed)
self.__project = project
if project:
self.__tree_model = Gtk.TreeListModel.new(
project,
False,
False,
self.__tree_model_create_func,
None
)
self.single_selection.props.model = self.__tree_model
self.__project.connect("selection-changed", self.__on_project_selection_changed)
else:
self.single_selection.props.model = None
def __tree_model_create_func(self, item, data):
if isinstance(item, CmbObject):
return item
elif isinstance(item, CmbUI):
return item
elif isinstance(item, CmbGResource):
return item
elif isinstance(item, CmbPath):
return item
return None
def __on_selected_item_notify(self, single_selection, pspec):
if self.__in_selection_change or self.__project is None:
return
list_item = single_selection.get_selected_item()
position = single_selection.get_selected()
if list_item is None:
self.__project.set_selection([])
return
item = list_item.get_item()
self.activate_action("list.activate-item", GLib.Variant("u", position))
if item and not isinstance(item, CmbPath):
item = list_item.get_item()
self.__project.set_selection([item])
else:
self.__project.set_selection([])
def _on_factory_setup(self, factory, list_item):
expander = CmbTreeExpander()
list_item.set_child(expander)
def _on_factory_bind(self, factory, list_item):
row = list_item.get_item()
expander = list_item.get_child()
expander.set_list_row(row)
expander.update_bind()
def _on_factory_unbind(self, factory, list_item):
expander = list_item.get_child()
expander.clear_bind()
def __get_tree_expander(self, x, y):
pick = self.pick(x, y, Gtk.PickFlags.DEFAULT)
if pick is None:
return None
if isinstance(pick, Gtk.TreeExpander):
return pick
child = pick.get_first_child()
if child and isinstance(child, Gtk.TreeExpander):
return child
parent = pick.props.parent
if parent and isinstance(parent, Gtk.TreeExpander):
return parent
return None
def __on_activate(self, column_view, position):
item = self.__tree_model.get_item(position)
item.set_expanded(not item.get_expanded())
def do_query_tooltip(self, x, y, keyboard_mode, tooltip):
expander = self.__get_tree_expander(x, y)
if expander is None:
return False
obj = expander.get_item()
if isinstance(obj, CmbObject):
msg = obj.version_warning
if msg:
tooltip.set_text(msg)
return True
return False

View File

@ -1,7 +1,7 @@
#
# CmbListStore - Cambalache List Store
# CmbMessageNotificationView
#
# Copyright (C) 2021 Juan Pablo Ugarte
# Copyright (C) 2025 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -23,26 +23,28 @@
# SPDX-License-Identifier: LGPL-2.1-only
#
from cambalache import getLogger
from gi.repository import GObject, Gtk
from .cmb_notification import CmbMessageNotification
logger = getLogger(__name__)
class CmbListStore(Gtk.ListStore):
__gtype_name__ = "CmbListStore"
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_message_notification_view.ui")
class CmbMessageNotificationView(Gtk.Box):
__gtype_name__ = "CmbMessageNotificationView"
table = GObject.Property(type=str)
query = GObject.Property(type=str)
project = GObject.Property(type=GObject.GObject)
notification = GObject.Property(
type=CmbMessageNotification, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY
)
# Message
title_label = Gtk.Template.Child()
message_label = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
data = self.project._get_table_data(self.table)
self.set_column_types(data["types"])
self.__populate()
def __populate(self):
c = self.project.db.cursor()
for row in c.execute(self.query):
self.append(row)
c.close()
notification = self.notification
self.title_label.props.label = f"<b>{notification.title}</b>"
self.message_label.props.label = notification.message

View File

@ -0,0 +1,22 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_message_notification_view.ui -->
<requires lib="gtk" version="4.0"/>
<template class="CmbMessageNotificationView" parent="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="title_label">
<property name="halign">start</property>
<property name="use-markup">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="message_label">
<property name="halign">start</property>
<property name="use-markup">True</property>
</object>
</child>
</template>
</interface>

View File

@ -0,0 +1,378 @@
#
# Cambalache notification system
#
# Copyright (C) 2025 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
import os
import json
import threading
import http.client
import time
import platform
from uuid import uuid4
from urllib.parse import urlparse
from .config import VERSION
from gi.repository import GObject, GLib, Gio, Gdk, Gtk, Adw, HarfBuzz
from cambalache import getLogger
from . import utils
logger = getLogger(__name__)
class CmbBaseData(GObject.GObject):
def __init__(self, **kwargs):
for prop in self.list_properties():
name = prop.name.replace("-", "_")
if name not in kwargs:
continue
value = kwargs[name]
if isinstance(value, dict) and prop.value_type in GTYPE_PTYHON:
Klass = GTYPE_PTYHON[prop.value_type]
kwargs[name] = Klass(**value)
super().__init__(**kwargs)
def dict(self):
retval = {}
for prop in self.list_properties():
name = prop.name.replace("-", "_")
value = self.get_property(prop.name)
if prop.value_type in GTYPE_PTYHON:
retval[name] = value.dict() if value else None
elif not isinstance(value, GObject.Object):
retval[name] = value
return retval
class CmbPollData(CmbBaseData):
id = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
title = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
description = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
options = GObject.Property(type=object, flags=GObject.ParamFlags.READWRITE)
allowed_votes = GObject.Property(type=int, default=1, flags=GObject.ParamFlags.READWRITE)
start_date = GObject.Property(type=int, flags=GObject.ParamFlags.READWRITE)
end_date = GObject.Property(type=int, flags=GObject.ParamFlags.READWRITE)
class CmbPollResult(CmbBaseData):
votes = GObject.Property(type=object, flags=GObject.ParamFlags.READWRITE)
total = GObject.Property(type=int, flags=GObject.ParamFlags.READWRITE)
GTYPE_PTYHON = {CmbPollData.__gtype__: CmbPollData, CmbPollResult.__gtype__: CmbPollResult}
class CmbNotification(CmbBaseData):
center = GObject.Property(type=GObject.Object, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
type = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
start_date = GObject.Property(type=int, flags=GObject.ParamFlags.READWRITE)
end_date = GObject.Property(type=int, flags=GObject.ParamFlags.READWRITE)
class CmbVersionNotification(CmbNotification):
version = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
release_notes = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
read_more_url = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
class CmbMessageNotification(CmbNotification):
title = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
message = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
class CmbPollNotification(CmbNotification):
poll = GObject.Property(type=CmbPollData, flags=GObject.ParamFlags.READWRITE)
results = GObject.Property(type=CmbPollResult, flags=GObject.ParamFlags.READWRITE)
my_votes = GObject.Property(type=object, flags=GObject.ParamFlags.READWRITE)
class CmbNotificationCenter(GObject.GObject):
__gsignals__ = {
"new-notification": (GObject.SignalFlags.RUN_FIRST, None, (CmbNotification,)),
}
# Settings
enabled = GObject.Property(type=bool, default=True, flags=GObject.ParamFlags.READWRITE)
uuid = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
next_request = GObject.Property(type=int, flags=GObject.ParamFlags.READWRITE)
notifications = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
store = GObject.Property(type=Gio.ListStore, flags=GObject.ParamFlags.READWRITE)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.retry_interval = 2
self.user_agent = self.__get_user_agent()
self.store = Gio.ListStore(item_type=CmbNotification)
self.settings = Gio.Settings(schema_id="ar.xjuan.Cambalache.notification")
for prop in ["enabled", "uuid", "next-request", "notifications"]:
self.settings.bind(prop, self, prop.replace("-", "_"), Gio.SettingsBindFlags.DEFAULT)
# Disable notifications if settings backend is ephemeral
if GObject.type_name(Gio.SettingsBackend.get_default()) == "GMemorySettingsBackend":
logger.info("Disabling notifications")
self.enabled = False
self.__load_notifications()
backend = urlparse(os.environ.get("CMB_NOTIFICATION_URL", "https://xjuan.ar:1934"))
self.REQUEST_INTERVAL = 4 if backend.hostname == "localhost" else 24 * 60 * 60
if backend.scheme == "https":
logger.info(f"Backend: {backend.scheme}://{backend.hostname}:{backend.port}")
self.connection = http.client.HTTPSConnection(backend.hostname, backend.port, timeout=8)
else:
self.connection = None
logger.warning(f"{backend.scheme} is not supported, only HTTPS")
return
# Ensure we have a UUID
if not self.uuid:
self.uuid = str(uuid4())
logger.info(f"User Agent: {self.user_agent}")
logger.info(f"UUID: {self.uuid}")
self._get_notification()
def __get_container(self):
if "FLATPAK_ID" in os.environ:
return "flatpak"
elif "APPIMAGE" in os.environ:
return "appimage"
elif "SNAP" in os.environ:
return "snap"
return None
def __get_user_agent(self):
u = platform.uname()
platform_strings = []
table = str.maketrans({",": "\\,"})
if u.system == "Linux":
release = platform.freedesktop_os_release()
system = f"Linux {release['ID']}"
if "VERSION_ID" in release:
system += f" {release['VERSION_ID']}"
if "VERSION_CODENAME" in release:
system += f" {release['VERSION_CODENAME']}"
else:
system = u.system
display_type = GObject.type_name(Gdk.Display.get_default())
backend = display_type.removeprefix("Gdk").removesuffix("Display")
lang = HarfBuzz.language_to_string(HarfBuzz.language_get_default())
extra = []
# Container type
container = self.__get_container()
if container:
extra.append(f"container {container}")
# GSettings backend
settings_backend = Gio.SettingsBackend.get_default()
gsettings_backend = GObject.type_name(settings_backend).removesuffix("SettingsBackend")
for name, lib in [("GLib", GLib), ("Gtk", Gtk), ("Adw", Adw)]:
extra.append(f"{name} {lib.MAJOR_VERSION}.{lib.MINOR_VERSION}.{lib.MICRO_VERSION}")
# Ignore node name as that is private and irrelevant information
for string in [system, u.release, u.version, u.machine, backend, gsettings_backend]:
if not string:
continue
platform_strings.append(string.translate(table))
return f"Cambalache/{VERSION} ({', '.join(platform_strings)}; {'; '.join(extra)}; {lang})"
def __load_notifications(self):
self.store.remove_all()
if not self.notifications:
return
notifications = json.loads(self.notifications)
now = utils.utcnow()
for data in notifications:
if "end_date" in data and now > data["end_date"]:
continue
self.store.append(self.__notification_from_dict(data))
def __save_notifications(self):
notifications = []
for notification in self.store:
notifications.append(notification.dict())
# Store in GSettings
self.notifications = json.dumps(notifications, indent=2, sort_keys=True)
def __notification_from_dict(self, data):
ntype = data.get("type", None)
if ntype == "version":
return CmbVersionNotification(center=self, **data)
elif ntype == "message":
return CmbMessageNotification(center=self, **data)
elif ntype == "poll":
return CmbPollNotification(center=self, **data)
def __get_notification_idle(self, data):
logger.debug(f"Got notification response {json.dumps(data, indent=2, sort_keys=True)}")
if "notification" in data:
notification = self.__notification_from_dict(data["notification"])
self.store.insert(0, notification)
self.__save_notifications()
self.emit("new-notification", notification)
now = int(time.time())
self.next_request = now + self.REQUEST_INTERVAL
self._get_notification()
return GLib.SOURCE_REMOVE
def __get_notification_thread(self):
headers = {
"User-Agent": self.user_agent,
"x-cambalache-uuid": self.uuid,
}
try:
logger.info(f"GET /notification {headers=}")
self.connection.request("GET", "/notification", headers=headers)
response = self.connection.getresponse()
assert response.status == 200
# Reset retry interval
self.retry_interval = 8
data = response.read().decode()
logger.info(f"response={data}")
if data:
GLib.idle_add(self.__get_notification_idle, json.loads(data))
except Exception as e:
# If it fails we just wait a bit before retrying
self.retry_interval *= 2
self.retry_interval = min(self.retry_interval, 256)
logger.info(f"Request error {e}, retrying in {self.retry_interval}s")
GLib.timeout_add_seconds(self.retry_interval, self._get_notification)
self.connection.close()
def __run_in_thread(self, function, *args, **kwargs):
if not self.connection:
logger.warning("No connection defined")
return
if not self.enabled:
logger.info("Notifications disabled")
return
thread = threading.Thread(target=function, args=args, kwargs=kwargs, daemon=True)
thread.start()
def _get_notification(self):
now = int(time.time())
if now >= self.next_request:
self.__run_in_thread(self.__get_notification_thread)
else:
GLib.timeout_add_seconds(self.next_request - now, self._get_notification)
def __poll_vote_idle(self, data):
logger.debug(f"Got vote response {data}")
poll_uuid = data["uuid"]
results = data["results"]
for notification in self.store:
if isinstance(notification, CmbPollNotification) and notification.poll.id == poll_uuid:
notification.results = CmbPollResult(**results)
self.__save_notifications()
break
return GLib.SOURCE_REMOVE
def __poll_vote_exception_idle(self, poll_uuid):
for notification in self.store:
if isinstance(notification, CmbPollNotification) and notification.poll.id == poll_uuid:
notification.my_votes = []
break
return GLib.SOURCE_REMOVE
def __poll_vote_thread(self, method, poll_uuid, votes=None):
headers = {"User-Agent": self.user_agent, "x-cambalache-uuid": self.uuid, "Content-type": "application/json"}
try:
payload = json.dumps({"votes": votes}) if method == "POST" else None
self.connection.request(method, f"/poll/{poll_uuid}", payload, headers)
response = self.connection.getresponse()
assert response.status == 200
data = response.read().decode()
GLib.idle_add(self.__poll_vote_idle, json.loads(data))
except Exception as e:
logger.warning(f"Error voting {e}")
GLib.idle_add(self.__poll_vote_exception_idle, poll_uuid)
self.connection.close()
def poll_vote(self, notification: CmbPollNotification, votes: list[int]):
if self.uuid is None:
return
notification.my_votes = votes
self.__run_in_thread(self.__poll_vote_thread, "POST", notification.poll.id, votes)
def poll_refresh_results(self, notification: CmbPollNotification):
if self.uuid is None:
return
self.__run_in_thread(self.__poll_vote_thread, "GET", notification.poll.id)
def remove(self, notification: CmbNotification):
valid, position = self.store.find(notification)
if valid:
self.store.remove(position)
self.__save_notifications()
notification_center = CmbNotificationCenter()

View File

@ -0,0 +1,60 @@
#
# CmbNotificationListRow
#
# Copyright (C) 2025 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
import datetime
from cambalache import getLogger
from gi.repository import GObject, Gtk
logger = getLogger(__name__)
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_notification_list_row.ui")
class CmbNotificationListRow(Gtk.ListBoxRow):
__gtype_name__ = "CmbNotificationListRow"
view = GObject.Property(type=Gtk.Widget, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
box = Gtk.Template.Child()
date_label = Gtk.Template.Child()
close_button = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.props.activatable = False
notification = self.view.notification
start_date = datetime.datetime.utcfromtimestamp(notification.start_date).strftime("%x")
self.date_label.set_label(f"<small>{start_date}</small>")
self.box.append(self.view)
@Gtk.Template.Callback("on_map")
def __on_map(self, w):
self.props.child.props.reveal_child = True
@Gtk.Template.Callback("on_close_button_clicked")
def __on_close_button_clicked(self, button):
notification = self.view.notification
notification.center.remove(notification)

View File

@ -0,0 +1,42 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_notification_list_row.ui -->
<requires lib="gtk" version="4.12"/>
<template class="CmbNotificationListRow" parent="GtkListBoxRow">
<signal name="map" handler="on_map"/>
<child>
<object class="GtkRevealer">
<child>
<object class="GtkBox" id="box">
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="spacing">4</property>
<property name="vexpand-set">True</property>
<child>
<object class="GtkLabel" id="date_label">
<property name="halign">end</property>
<property name="hexpand">True</property>
<property name="use-markup">True</property>
</object>
</child>
<child>
<object class="GtkButton" id="close_button">
<property name="icon-name">window-close-symbolic</property>
<signal name="clicked" handler="on_close_button_clicked"/>
<style>
<class name="flat"/>
<class name="compact"/>
<class name="close"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -0,0 +1,67 @@
#
# CmbNotificationListView
#
# Copyright (C) 2025 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from cambalache import getLogger
from gi.repository import GObject, Gtk
from .cmb_version_notification_view import CmbVersionNotificationView
from .cmb_message_notification_view import CmbMessageNotificationView
from .cmb_poll_notification_view import CmbPollNotificationView
from .cmb_notification_list_row import CmbNotificationListRow
from .cmb_notification import CmbNotificationCenter, CmbVersionNotification, CmbMessageNotification, CmbPollNotification
logger = getLogger(__name__)
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_notification_list_view.ui")
class CmbNotificationListView(Gtk.Box):
__gtype_name__ = "CmbNotificationListView"
notification_center = GObject.Property(type=CmbNotificationCenter, flags=GObject.ParamFlags.READWRITE)
list_box = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.connect("notify::notification-center", self.__on_notification_center_notify)
def __on_notification_center_notify(self, obj, pspec):
self.list_box.bind_model(self.notification_center.store, self.__create_widget_func)
GObject.Object.bind_property(
self.notification_center.store,
"n-items",
self.list_box,
"visible",
GObject.BindingFlags.SYNC_CREATE,
)
def __create_widget_func(self, item):
if isinstance(item, CmbVersionNotification):
view = CmbVersionNotificationView(notification=item)
elif isinstance(item, CmbMessageNotification):
view = CmbMessageNotificationView(notification=item)
elif isinstance(item, CmbPollNotification):
view = CmbPollNotificationView(notification=item)
return CmbNotificationListRow(view=view)

View File

@ -0,0 +1,26 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_notification_list_view.ui -->
<requires lib="gtk" version="4.6"/>
<template class="CmbNotificationListView" parent="GtkBox">
<property name="hexpand">True</property>
<child>
<object class="GtkScrolledWindow">
<property name="hexpand">True</property>
<property name="propagate-natural-height">True</property>
<property name="propagate-natural-width">True</property>
<child>
<object class="GtkListBox" id="list_box">
<property name="activate-on-single-click">False</property>
<property name="selection-mode">none</property>
<property name="show-separators">True</property>
<style>
<class name="notifications"/>
</style>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -62,7 +62,6 @@ class CmbObject(CmbBaseObject, Gio.ListModel):
self.__signals_dict = None
self.__data = None
self.__data_dict = None
self.position_layout_property = None
self.inline_property_id = None
self.version_warning = None
self.__is_template = False
@ -207,10 +206,6 @@ class CmbObject(CmbBaseObject, Gio.ListModel):
info=info,
)
# Keep a reference to the position layout property
if info.is_position:
self.position_layout_property = prop
self.__layout.append(prop)
# Dictionary of properties
@ -463,11 +458,17 @@ class CmbObject(CmbBaseObject, Gio.ListModel):
def remove_data(self, data):
try:
assert data.get_id_string() in self.data_dict
self.project.history_push(
_("Remove {key} from {name}").format(key=data.info.key, name=self.display_name_type)
)
self.project.db.execute(
"DELETE FROM object_data WHERE ui_id=? AND object_id=? AND owner_id=? AND data_id=? AND id=?;",
(self.ui_id, self.object_id, data.owner_id, data.data_id, data.id),
)
self.project.db.commit()
self.project.history_pop()
except Exception as e:
logger.warning(f"{self} Error removing data {data}: {e}")
return False
@ -535,23 +536,6 @@ class CmbObject(CmbBaseObject, Gio.ListModel):
# Set new position
db.execute("UPDATE object SET position=? WHERE ui_id=? AND object_id=?;", (position, self.ui_id, child.object_id))
# Update position layout property (Example GtkBox position in Gtk3)
if child.position_layout_property:
db.execute(
"""
UPDATE object_layout_property AS olp SET value=o.position
FROM object AS o
WHERE
o.ui_id=? AND
o.parent_id=? AND
olp.ui_id=o.ui_id AND
olp.object_id=o.parent_id AND
olp.child_id=o.object_id AND
olp.property_id=?;
""",
(self.ui_id, self.object_id, child.position_layout_property.property_id)
)
db.ignore_check_constraints = False
list_position = child.list_position
@ -562,8 +546,10 @@ class CmbObject(CmbBaseObject, Gio.ListModel):
self.items_changed(list_position, 0, 1)
self.items_changed(old_list_position+1, 1, 0)
else:
if old_list_position != list_position:
self.items_changed(old_list_position, 1, 0)
self.items_changed(list_position, 0, 1)
self.project._ignore_selection = False
self.project.history_pop()
@ -613,8 +599,11 @@ class CmbObject(CmbBaseObject, Gio.ListModel):
display_name = _("{name} (template)").format(name=name)
else:
inline_prop = self.inline_property_id
internal = self.internal
if inline_prop:
display_name = f"{type_id} <b>{inline_prop}</b> <i>{name}</i>"
elif internal:
display_name = f"{type_id} <b>{internal}</b> <i>{name}</i>"
else:
display_name = f"{type_id} <i>{name}</i>"

View File

@ -27,7 +27,7 @@ from gi.repository import GObject
from .cmb_objects_base import CmbBaseObjectData
from .cmb_type_info import CmbTypeDataInfo
from cambalache import getLogger
from cambalache import getLogger, _
logger = getLogger(__name__)
@ -194,11 +194,17 @@ class CmbObjectData(CmbBaseObjectData):
def remove_data(self, data):
try:
assert data in self.children
self.project.history_push(
_("Remove {key} from {name}").format(key=data.info.key, name=self.object.display_name_type)
)
self.project.db.execute(
"DELETE FROM object_data WHERE ui_id=? AND object_id=? AND owner_id=? AND data_id=? AND id=?;",
(self.ui_id, self.object_id, data.owner_id, data.data_id, data.id),
)
self.project.db.commit()
self.project.history_pop()
except Exception as e:
logger.warning(f"{self} Error removing data {data}: {e}")
return False

View File

@ -31,6 +31,12 @@ from .control import cmb_create_editor
from cambalache import _
# Everyone knows that debugging is twice as hard as writing a program in the first place.
# So if youre as clever as you can be when you write it, how will you ever debug it?
# -- Brian Kernighan, 1974
#
# TODO: rewrite this!
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_object_data_editor.ui")
class CmbObjectDataEditor(Gtk.Box):
__gtype_name__ = "CmbObjectDataEditor"
@ -69,7 +75,7 @@ class CmbObjectDataEditor(Gtk.Box):
def __on_remove_clicked(self, button):
if self.info:
self.object.remove_data(self.__data)
else:
elif self.__data:
self.__data.parent.remove_data(self.__data)
@GObject.Property(type=GObject.Object)
@ -138,7 +144,6 @@ class CmbObjectDataEditor(Gtk.Box):
self.__update_view()
def __on_data_removed(self, obj, data):
if self.object and self.info:
self.__remove_data_editor(data)
def __ensure_object_data(self, history_message):

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.1 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_object_data_editor.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->
@ -70,7 +70,6 @@
<object class="GtkGrid" id="grid">
<property name="column-spacing">4</property>
<property name="row-spacing">4</property>
<property name="vexpand">1</property>
</object>
</child>
</template>

View File

@ -260,7 +260,7 @@ It has to be exposed by your application with GtkBuilder expose_object method."
hexpand=True,
object=obj,
data=data,
info=None if data else info.data[data_key],
info=info.data[data_key],
)
grid.attach(editor, 0, i, 2, 1)

View File

@ -3,7 +3,7 @@
#
# Cambalache Base Object wrappers
#
# Copyright (C) 2021-2022 Juan Pablo Ugarte
# Copyright (C) 2021-2024 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -22,6 +22,8 @@
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from gi.repository import GObject
from .cmb_base import CmbBase
@ -81,9 +83,7 @@ class CmbBasePropertyInfo(CmbBase):
disable_inline_object = GObject.Property(
type=bool, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, default=False
)
is_position = GObject.Property(
type=bool, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, default=False
)
deprecated = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
required = GObject.Property(
type=bool, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, default=False
)
@ -113,7 +113,7 @@ class CmbBasePropertyInfo(CmbBase):
deprecated_version,
translatable,
disable_inline_object,
is_position,
deprecated,
required,
workspace_default,
original_owner_id,
@ -134,7 +134,7 @@ class CmbBasePropertyInfo(CmbBase):
deprecated_version=deprecated_version,
translatable=translatable,
disable_inline_object=disable_inline_object,
is_position=is_position,
deprecated=deprecated,
required=required,
workspace_default=workspace_default,
original_owner_id=original_owner_id,
@ -285,6 +285,30 @@ class CmbTypeChildInfo(CmbBase):
)
class CmbBaseTypeInternalChildInfo(CmbBase):
__gtype_name__ = "CmbBaseTypeInternalChildInfo"
type_id = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
internal_child_id = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
internal_parent_id = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
internal_type = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
creation_property_id = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def from_row(cls, project, type_id, internal_child_id, internal_parent_id, internal_type, creation_property_id):
return cls(
project=project,
type_id=type_id,
internal_child_id=internal_child_id,
internal_parent_id=internal_parent_id,
internal_type=internal_type,
creation_property_id=creation_property_id,
)
class CmbBaseUI(CmbBase):
__gtype_name__ = "CmbBaseUI"
@ -429,6 +453,97 @@ class CmbBaseCSS(CmbBase):
self.db_set("UPDATE css SET is_global=? WHERE (css_id) IS (?);", (self.css_id,), value)
class CmbBaseGResource(CmbBase):
__gtype_name__ = "CmbBaseGResource"
gresource_id = GObject.Property(type=int, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
resource_type = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def from_row(
cls,
project,
gresource_id,
resource_type,
parent_id,
position,
gresources_filename,
gresource_prefix,
file_filename,
file_compressed,
file_preprocess,
file_alias,
):
return cls(project=project, gresource_id=gresource_id)
@GObject.Property(type=int)
def parent_id(self):
return self.db_get("SELECT parent_id FROM gresource WHERE (gresource_id) IS (?);", (self.gresource_id,))
@parent_id.setter
def _set_parent_id(self, value):
self.db_set("UPDATE gresource SET parent_id=? WHERE (gresource_id) IS (?);", (self.gresource_id,), value)
@GObject.Property(type=int)
def position(self):
return self.db_get("SELECT position FROM gresource WHERE (gresource_id) IS (?);", (self.gresource_id,))
@position.setter
def _set_position(self, value):
self.db_set("UPDATE gresource SET position=? WHERE (gresource_id) IS (?);", (self.gresource_id,), value)
@GObject.Property(type=str)
def gresources_filename(self):
return self.db_get("SELECT gresources_filename FROM gresource WHERE (gresource_id) IS (?);", (self.gresource_id,))
@gresources_filename.setter
def _set_gresources_filename(self, value):
self.db_set("UPDATE gresource SET gresources_filename=? WHERE (gresource_id) IS (?);", (self.gresource_id,), value)
@GObject.Property(type=str)
def gresource_prefix(self):
return self.db_get("SELECT gresource_prefix FROM gresource WHERE (gresource_id) IS (?);", (self.gresource_id,))
@gresource_prefix.setter
def _set_gresource_prefix(self, value):
self.db_set("UPDATE gresource SET gresource_prefix=? WHERE (gresource_id) IS (?);", (self.gresource_id,), value)
@GObject.Property(type=str)
def file_filename(self):
return self.db_get("SELECT file_filename FROM gresource WHERE (gresource_id) IS (?);", (self.gresource_id,))
@file_filename.setter
def _set_file_filename(self, value):
self.db_set("UPDATE gresource SET file_filename=? WHERE (gresource_id) IS (?);", (self.gresource_id,), value)
@GObject.Property(type=bool, default=False)
def file_compressed(self):
return self.db_get("SELECT file_compressed FROM gresource WHERE (gresource_id) IS (?);", (self.gresource_id,))
@file_compressed.setter
def _set_file_compressed(self, value):
self.db_set("UPDATE gresource SET file_compressed=? WHERE (gresource_id) IS (?);", (self.gresource_id,), value)
@GObject.Property(type=str)
def file_preprocess(self):
return self.db_get("SELECT file_preprocess FROM gresource WHERE (gresource_id) IS (?);", (self.gresource_id,))
@file_preprocess.setter
def _set_file_preprocess(self, value):
self.db_set("UPDATE gresource SET file_preprocess=? WHERE (gresource_id) IS (?);", (self.gresource_id,), value)
@GObject.Property(type=str)
def file_alias(self):
return self.db_get("SELECT file_alias FROM gresource WHERE (gresource_id) IS (?);", (self.gresource_id,))
@file_alias.setter
def _set_file_alias(self, value):
self.db_set("UPDATE gresource SET file_alias=? WHERE (gresource_id) IS (?);", (self.gresource_id,), value)
class CmbBaseProperty(CmbBase):
__gtype_name__ = "CmbBaseProperty"

118
cambalache/cmb_path.py Normal file
View File

@ -0,0 +1,118 @@
#
# CmbPath
#
# Copyright (C) 2024 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from gi.repository import GObject, Gio
from .cmb_base import CmbBase
from cambalache import _, getLogger
logger = getLogger(__name__)
class CmbPath(CmbBase, Gio.ListModel):
__gtype_name__ = "CmbPath"
path_parent = GObject.Property(type=CmbBase, flags=GObject.ParamFlags.READWRITE)
path = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
def __init__(self, **kwargs):
super().__init__(**kwargs)
# GListModel
self.__items = []
self.__path_items = {}
def __bool__(self):
return True
def __str__(self):
return f"CmbPath<{self.path}>"
@GObject.Property(type=str)
def display_name(self):
return self.path or _("{n} unsaved files").format(n=self.n_items)
def get_path_item(self, directory):
return self.__path_items.get(directory, None)
def add_item(self, item):
if item in self.__items:
return
display_name = item.display_name
is_path = isinstance(item, CmbPath)
if is_path:
self.__path_items[item.path] = item
i = 0
for list_item in self.__items:
if is_path:
if not isinstance(list_item, CmbPath):
break
if display_name < list_item.display_name:
break
elif not isinstance(list_item, CmbPath) and display_name < list_item.display_name:
break
i += 1
item.path_parent = self
self.__items.insert(i, item)
self.items_changed(i, 0, 1)
if not self.path:
self.notify("display-name")
def remove_item(self, item):
if item not in self.__items:
return
if isinstance(item, CmbPath) and item.path in self.__path_items:
del self.__path_items[item.path]
item.path_parent = None
i = self.__items.index(item)
self.__items.pop(i)
self.items_changed(i, 1, 0)
if not self.path:
self.notify("display-name")
# GListModel iface
@GObject.Property(type=int)
def n_items(self):
return len(self.__items)
def do_get_item(self, position):
return self.__items[position] if position < len(self.__items) else None
def do_get_item_type(self):
return CmbBase
def do_get_n_items(self):
return self.n_items

View File

@ -0,0 +1,142 @@
#
# CmbPollNotificationView
#
# Copyright (C) 2025 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
import datetime
from cambalache import _, getLogger
from gi.repository import GObject, Gtk
from . import utils
from .cmb_notification import CmbPollNotification
from .cmb_poll_option_check import CmbPollOptionCheck
logger = getLogger(__name__)
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_poll_notification_view.ui")
class CmbPollNotificationView(Gtk.Box):
__gtype_name__ = "CmbPollNotificationView"
notification = GObject.Property(
type=CmbPollNotification, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY
)
# Poll
title_label = Gtk.Template.Child()
description_label = Gtk.Template.Child()
option_box = Gtk.Template.Child()
total_label = Gtk.Template.Child()
end_date_label = Gtk.Template.Child()
refresh_button = Gtk.Template.Child()
def __init__(self, **kwargs):
self.__option_checks = []
self.__updating = False
super().__init__(**kwargs)
notification = self.notification
poll = notification.poll
active = utils.utcnow() < poll.end_date
self.title_label.props.label = f"<b>{poll.title}</b>"
self.description_label.props.label = poll.description
close_msg = _("<small>• Closes on {date}</small>") if active else _("<small>• Closed on {date}</small>")
end_date = datetime.datetime.fromtimestamp(poll.end_date)
self.end_date_label.props.label = close_msg.format(date=end_date.strftime("%x"))
self.end_date_label.props.tooltip_text = end_date.strftime("%c")
first_check = None
n_option = 0
for option in poll.options:
button = CmbPollOptionCheck(option=option, sensitive=active)
if poll.allowed_votes == 1:
if first_check is None:
first_check = button
else:
button.set_group(first_check)
button.connect("toggled", self.__on_check_button_toggled, n_option)
self.__option_checks.append(button)
self.option_box.append(button)
n_option += 1
self.__update_results()
notification.connect("notify", self.__on_poll_notify)
def __on_check_button_toggled(self, button, n_option):
allowed_votes = self.notification.poll.allowed_votes
if self.__updating or (allowed_votes == 1 and not button.props.active):
return
votes = []
for i, check in enumerate(self.__option_checks):
if check.props.active:
votes.append(i)
if allowed_votes > 1:
not_done = len(votes) < allowed_votes
for i, check in enumerate(self.__option_checks):
if not_done:
check.set_sensitive(True)
elif not check.props.active:
check.set_sensitive(False)
self.notification.center.poll_vote(self.notification, votes)
def __on_poll_notify(self, notification, pspec):
if pspec.name in ["my-votes", "results"]:
self.__update_results()
def __update_results(self):
notification = self.notification
results = notification.results
my_votes = notification.my_votes
if not results or not my_votes:
self.total_label.props.label = ""
for check in self.__option_checks:
check.fraction = -1
return
self.__updating = True
votes = results.votes
total = results.total
for i, check in enumerate(self.__option_checks):
check.set_active(i in my_votes)
check.fraction = votes[i] / total if total else 0
self.total_label.props.label = _("<small>• {total} vote</small>").format(total=results.total)
self.__updating = False
@Gtk.Template.Callback("on_refresh_button_clicked")
def __on_refresh_button_clicked(self, button):
self.notification.center.poll_refresh_results(self.notification)

View File

@ -0,0 +1,64 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_poll_notification_view.ui -->
<requires lib="gtk" version="4.0"/>
<template class="CmbPollNotificationView" parent="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="title_label">
<property name="halign">start</property>
<property name="use-markup">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="description_label">
<property name="halign">start</property>
<property name="use-markup">True</property>
</object>
</child>
<child>
<object class="GtkBox" id="option_box">
<property name="orientation">vertical</property>
<property name="spacing">4</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="spacing">4</property>
<property name="vexpand-set">True</property>
<child>
<object class="GtkButton" id="refresh_button">
<property name="child">
<object class="GtkLabel">
<property name="label">&lt;small&gt;Refresh&lt;/small&gt;</property>
<property name="use-markup">True</property>
</object>
</property>
<signal name="clicked" handler="on_refresh_button_clicked"/>
<style>
<class name="flat"/>
<class name="compact"/>
<class name="link"/>
<class name="text-button"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel" id="total_label">
<property name="use-markup">True</property>
<style>
<class name="link"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel" id="end_date_label">
<property name="use-markup">True</property>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -0,0 +1,76 @@
#
# CmbPollOptionCheck
#
# Copyright (C) 2025 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from cambalache import getLogger
from gi.repository import GLib, GObject, Gtk
logger = getLogger(__name__)
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_poll_option_check.ui")
class CmbPollOptionCheck(Gtk.CheckButton):
__gtype_name__ = "CmbPollOptionCheck"
label = Gtk.Template.Child()
bar = Gtk.Template.Child()
def __init__(self, **kwargs):
self.__fraction = None
self.__tick_id = None
super().__init__(**kwargs)
@GObject.Property(type=str)
def option(self):
return self.label.props.label
@option.setter
def _set_option(self, option):
self.label.props.label = option
@GObject.Property(type=float)
def fraction(self):
return self.__fraction
@fraction.setter
def _set_fraction(self, fraction):
self.__fraction = fraction
if fraction < 0:
self.bar.props.visible = False
else:
self.bar.props.visible = True
if self.__tick_id is None:
self.__tick_id = self.add_tick_callback(self.__update_fraction)
def __update_fraction(self, widget, frame_clock):
if self.bar.props.fraction < self.__fraction:
self.bar.props.fraction = min(self.__fraction, self.bar.props.fraction + 0.08)
elif self.bar.props.fraction > self.__fraction:
self.bar.props.fraction = max(self.__fraction, self.bar.props.fraction - 0.08)
else:
self.__tick_id = None
return GLib.SOURCE_REMOVE
return GLib.SOURCE_CONTINUE

View File

@ -0,0 +1,23 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name r.ui -->
<requires lib="gtk" version="4.0"/>
<template class="CmbPollOptionCheck" parent="GtkCheckButton">
<child>
<object class="GtkBox">
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="valign">center</property>
<child>
<object class="GtkLabel" id="label">
<property name="halign">start</property>
</object>
</child>
<child>
<object class="GtkProgressBar" id="bar"/>
</child>
</object>
</child>
</template>
</interface>

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,9 @@ from gi.repository import GObject
from .cmb_objects_base import CmbBaseProperty
from .cmb_property_info import CmbPropertyInfo
from . import utils
from cambalache import _
from cambalache import _, getLogger
logger = getLogger(__name__)
class CmbProperty(CmbBaseProperty):
@ -64,15 +66,96 @@ class CmbProperty(CmbBaseProperty):
@value.setter
def _set_value(self, value):
self.__update_values(value, self.bind_property)
self.__update_values(value, self.translatable, self.translation_context, self.translation_comments, self.bind_property)
@GObject.Property(type=bool, default=False)
def translatable(self):
return super().translatable
@translatable.setter
def _set_translatable(self, value):
self.__update_values(self.value, value, self.translation_context, self.translation_comments, self.bind_property)
@GObject.Property(type=str)
def translation_context(self):
return self.db_get(
"SELECT translation_context FROM object_property WHERE (ui_id, object_id, owner_id, property_id) IS (?, ?, ?, ?);",
(
self.ui_id,
self.object_id,
self.owner_id,
self.property_id,
),
)
@translation_context.setter
def _set_translation_context(self, value):
self.__update_values(self.value, self.translatable, value, self.translation_comments, self.bind_property)
@GObject.Property(type=str)
def translation_comments(self):
return self.db_get(
"SELECT translation_comments FROM object_property WHERE (ui_id, object_id, owner_id, property_id) IS (?, ?, ?, ?);",
(
self.ui_id,
self.object_id,
self.owner_id,
self.property_id,
),
)
@translation_comments.setter
def _set_translation_comments(self, value):
self.__update_values(self.value, self.translatable, self.translation_context, value, self.bind_property)
def reset(self):
if self.info.internal_child:
self.project.history_push(_("Unset {obj} {prop} {prop_type}").format(**self.__get_msgs()))
self.project.db.execute(
"DELETE FROM object_property WHERE ui_id=? AND object_id=? AND owner_id=? AND property_id=?;",
(self.ui_id, self.object_id, self.owner_id, self.property_id),
)
self.__update_internal_child()
def __update_values(self, value, bind_property):
if self.info.internal_child:
self.project.history_pop()
def __update_internal_child(self):
internal_info = self.info.internal_child
if internal_info and internal_info.internal_parent_id:
logger.warning("Adding an internal child within an internal child automatically is not implemented")
return
elif internal_info is None:
return
value = self.value
child_id = self.db_get(
"SELECT object_id FROM object WHERE ui_id=? AND parent_id=? AND internal=?",
(self.ui_id, self.object_id, internal_info.internal_child_id)
)
if value and not child_id:
self.project.add_object(
self.ui_id,
internal_info.internal_type,
parent_id=self.object_id,
internal=internal_info.internal_child_id
)
elif child_id:
internal_child = self.project.get_object_by_id(self.ui_id, child_id)
if internal_child:
self.project.remove_object(internal_child, allow_internal_removal=True)
def __get_msgs(self, value=None):
return {
"obj": self.object.display_name_type,
"prop": self.property_id,
"prop_type": _("property"),
"value": str(value)
}
def __update_values(self, value, translatable, translation_context, translation_comments, bind_property):
c = self.project.db.cursor()
bind_source_id, bind_owner_id, bind_property_id = (None, None, None)
@ -82,15 +165,22 @@ class CmbProperty(CmbBaseProperty):
bind_property_id = bind_property.property_id
if (
value is None or value == self.info.default_value or (self.info.is_object and value == 0)
) and bind_property is None:
(value is None or value == self.info.default_value or (self.info.is_object and value == 0)) and
bind_property is None and
not translatable and
translation_context is None and
translation_comments is None
):
self.reset()
else:
if (
value is None
and bind_source_id == self.bind_source_id
and bind_owner_id == self.bind_owner_id
and bind_property_id == self.bind_property_id
value == self.value and
translatable == self.translatable and
translation_context == self.translation_context and
translation_comments == self.translation_comments and
bind_source_id == self.bind_source_id and
bind_owner_id == self.bind_owner_id and
bind_property_id == self.bind_property_id
):
return
@ -101,14 +191,23 @@ class CmbProperty(CmbBaseProperty):
)
if count:
if self.info.internal_child:
self.project.history_push(_("Update {obj} {prop} {prop_type} to {value}").format(**self.__get_msgs(value)))
c.execute(
"""
UPDATE object_property
SET value=?, bind_source_id=?, bind_owner_id=?, bind_property_id=?
SET
value=?,
translatable=?, translation_context=?, translation_comments=?,
bind_source_id=?, bind_owner_id=?, bind_property_id=?
WHERE ui_id=? AND object_id=? AND owner_id=? AND property_id=?;
""",
(
value,
translatable,
translation_context,
translation_comments,
bind_source_id,
bind_owner_id,
bind_property_id,
@ -119,11 +218,19 @@ class CmbProperty(CmbBaseProperty):
),
)
else:
if self.info.internal_child:
self.project.history_push(_("Set {obj} {prop} {prop_type} to {value}").format(**self.__get_msgs(value)))
c.execute(
"""
INSERT INTO object_property
(ui_id, object_id, owner_id, property_id, value, bind_source_id, bind_owner_id, bind_property_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
(
ui_id, object_id, owner_id, property_id,
value,
translatable, translation_context, translation_comments,
bind_source_id, bind_owner_id, bind_property_id
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
""",
(
self.ui_id,
@ -131,12 +238,20 @@ class CmbProperty(CmbBaseProperty):
self.owner_id,
self.property_id,
value,
translatable,
translation_context,
translation_comments,
bind_source_id,
bind_owner_id,
bind_property_id,
),
)
self.__update_internal_child()
if self.info.internal_child:
self.project.history_pop()
if self._init is False:
self.object._property_changed(self)
@ -163,7 +278,7 @@ class CmbProperty(CmbBaseProperty):
@bind_property.setter
def _set_bind_property(self, bind_property):
self.__update_values(self.value, bind_property)
self.__update_values(self.value, self.translatable, self.translation_context, self.translation_comments, bind_property)
self.project._object_property_binding_changed(self.object, self)
def _update_version_warning(self):

View File

@ -23,6 +23,7 @@
# SPDX-License-Identifier: LGPL-2.1-only
#
from gi.repository import GObject
from .cmb_objects_base import CmbBasePropertyInfo
from cambalache import getLogger
@ -31,6 +32,8 @@ logger = getLogger(__name__)
class CmbPropertyInfo(CmbBasePropertyInfo):
internal_child = GObject.Property(type=GObject.GObject, flags=GObject.ParamFlags.READWRITE)
def __init__(self, **kwargs):
super().__init__(**kwargs)

View File

@ -138,13 +138,17 @@ class CmbSignalEditor(Gtk.Box):
if data_obj:
signal.user_data = data_obj.object_id
name = data_obj.name
signal.swap = True
else:
signal.user_data = 0
signal.swap = False
self.treestore[iter_][Col.SWAP.value] = signal.swap
name = ""
self.treestore[iter_][Col.USER_DATA.value] = name
else:
signal.user_data = 0
signal.swap = False
self.treestore[iter_][Col.USER_DATA.value] = ""
@Gtk.Template.Callback("on_swap_toggled")

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.1 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_signal_editor.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->
@ -37,9 +37,6 @@
<property name="focusable">1</property>
<property name="model">treestore</property>
<property name="tooltip-column">9</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="signal_id_column">
<property name="min-width">64</property>

View File

@ -0,0 +1,266 @@
#
# CmbTreeExpander
#
# Copyright (C) 2024 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from gi.repository import GObject, Gdk, Gtk, Graphene
from .cmb_ui import CmbUI
from .cmb_object import CmbObject
from .cmb_css import CmbCSS
from .cmb_path import CmbPath
from cambalache import _
class CmbTreeExpander(Gtk.TreeExpander):
__gtype_name__ = "CmbTreeExpander"
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.__project = None
self.__drop_before = None
self.label = Gtk.Inscription(hexpand=True)
self.set_child(self.label)
self.__parent = None
self.__drag_source = None
self.__drop_target = None
def update_bind(self):
item = self.props.item
self.__parent = self.props.parent
# Drag source
self.__drag_source = Gtk.DragSource()
self.__drag_source.connect("prepare", self.__on_drag_prepare)
self.__drag_source.connect("drag-begin", self.__on_drag_begin)
self.__parent.add_controller(self.__drag_source)
# Drop target
self.__drop_target = Gtk.DropTarget.new(type=GObject.TYPE_NONE, actions=Gdk.DragAction.COPY)
self.__drop_target.set_gtypes([CmbObject, CmbUI])
self.__drop_target.connect("accept", self.__on_drop_accept)
self.__drop_target.connect("motion", self.__on_drop_motion)
self.__drop_target.connect("drop", self.__on_drop_drop)
self.__parent.add_controller(self.__drop_target)
# Handle label
self.__update_label(item)
item.connect("notify::display-name", self.__on_item_display_name_notify)
self.__project = item.project
if isinstance(item, CmbCSS):
self.props.hide_expander = True
return
elif isinstance(item, CmbPath):
self.props.hide_expander = False
self.add_css_class("cmb-path" if item.path else "cmb-unsaved-path")
return
self.props.hide_expander = item.props.n_items == 0
item.connect("notify::n-items", self.__on_item_n_items_notify)
def clear_bind(self):
item = self.props.item
if self.__parent:
self.__parent.remove_controller(self.__drag_source)
self.__parent.remove_controller(self.__drop_target)
self.__parent = None
self.__drag_source = None
self.__drop_target = None
self.remove_css_class("cmb-path")
self.remove_css_class("cmb-unsaved-path")
item.disconnect_by_func(self.__on_item_display_name_notify)
if isinstance(item, CmbCSS) or isinstance(item, CmbPath):
return
item.disconnect_by_func(self.__on_item_n_items_notify)
def __on_item_n_items_notify(self, item, pspec):
self.props.hide_expander = item.props.n_items == 0
def __on_item_display_name_notify(self, item, pspec):
self.__update_label(item)
def __update_label(self, item):
self.label.set_markup(item.props.display_name or "")
# Drop target callbacks
def __get_drop_before(self, widget, x, y):
h = widget.get_height()
if y < h/4:
return True
if y > h * 0.8:
return False
return None
def __on_object_drop_accept(self, drop, item):
origin_item = drop.get_drag()._item
if origin_item == item:
return None
if not isinstance(item, CmbObject):
return None
# Ignore if its the same parent
if origin_item.parent_id == item.object_id:
return None
return origin_item
def __on_drop_accept(self, target, drop):
item = self.props.item
origin_item = drop.get_drag()._item
if isinstance(item, CmbObject):
origin_item = self.__on_object_drop_accept(drop, item)
self.__drop_before = None
if origin_item is None or item.parent is None:
return False
return self.__project._check_can_add(origin_item.type_id, item.parent.type_id)
elif isinstance(item, CmbUI):
if origin_item == item:
return False
# Ignore if its the same UI and item is already a toplevel
if origin_item.ui_id == item.ui_id and origin_item.parent_id is None:
return False
return True
def __on_drop_motion(self, target, x, y):
item = self.props.item
if isinstance(item, CmbObject):
row_widget = target.get_widget()
drop_before = self.__get_drop_before(row_widget, x, y)
if self.__drop_before == drop_before:
return Gdk.DragAction.COPY
row_widget.remove_css_class("drop-before")
row_widget.remove_css_class("drop-after")
self.__drop_before = drop_before
if drop_before is not None:
row_widget.add_css_class("drop-before" if drop_before else "drop-after")
return Gdk.DragAction.COPY
def __on_drop_drop(self, target, origin_item, x, y):
row_widget = target.get_widget()
item = self.props.item
if isinstance(item, CmbObject):
drop_before = self.__get_drop_before(row_widget, x, y)
if drop_before is None:
# TODO: handle dragging from one UI to another
if origin_item.ui_id != item.ui_id:
return
if origin_item.parent_id != item.object_id:
self.__project.history_push(
_("Move {name} to {target}").format(name=origin_item.display_name, target=item.display_name)
)
origin_item.parent_id = item.object_id
self.__project.history_pop()
return
if origin_item.parent_id != item.parent_id:
if drop_before:
msg = _("Move {name} before {target}").format(name=origin_item.display_name, target=item.display_name)
else:
msg = _("Move {name} after {target}").format(name=origin_item.display_name, target=item.display_name)
self.__project.history_push(msg)
origin_item.parent_id = item.parent_id
else:
msg = None
parent = item.parent
origin_position = origin_item.position
target_position = item.position
if origin_position > target_position:
if drop_before:
position = item.position
else:
position = item.position + 1
else:
if drop_before:
position = item.position - 1
else:
position = item.position
parent.reorder_child(origin_item, position)
if msg:
self.__project.history_pop()
self.__project.set_selection([origin_item])
elif isinstance(item, CmbUI):
if origin_item.ui_id == item.ui_id:
self.__project.history_push(_("Move {name} as toplevel").format(name=origin_item.display_name))
origin_item.parent_id = 0
self.__project.history_pop()
else:
# TODO: Use copy/paste to move across UI files
pass
# Drag Source callbacks
def __on_drag_prepare(self, drag_source, x, y):
item = self.props.item
# Only CmbObject start a drag
if not isinstance(item, CmbObject):
return None
self.__drag_point = Graphene.Point()
self.__drag_point.x = x
self.__drag_point.y = y
return Gdk.ContentProvider.new_for_value(self.props.item)
def __on_drag_begin(self, drag_source, drag):
drag._item = self.props.item
valid, p = self.__parent.compute_point(self.label, self.__drag_point)
if valid:
drag_source.set_icon(Gtk.WidgetPaintable.new(self.label), p.x, p.y)

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.1 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_type_chooser.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.93.0 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_type_chooser_widget.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->

View File

@ -29,6 +29,7 @@ from .cmb_objects_base import (
CmbBaseTypeInfo,
CmbBaseTypeDataInfo,
CmbBaseTypeDataArgInfo,
CmbBaseTypeInternalChildInfo,
CmbTypeChildInfo,
CmbSignalInfo,
)
@ -59,6 +60,15 @@ class CmbTypeDataInfo(CmbBaseTypeDataInfo):
return f"CmbTypeDataArgInfo<{self.owner_id}>::{self.key}"
class CmbTypeInternalChildInfo(CmbBaseTypeInternalChildInfo):
def __init__(self, **kwargs):
self.children = {}
super().__init__(**kwargs)
def __str__(self):
return f"CmbTypeInternalChildInfo<{self.type_id}>::{self.internal_child_id}"
class CmbTypeInfo(CmbBaseTypeInfo):
__gtype_name__ = "CmbTypeInfo"
@ -76,6 +86,7 @@ class CmbTypeInfo(CmbBaseTypeInfo):
self.properties = self.__init_properties_signals(CmbPropertyInfo, "property")
self.signals = self.__init_properties_signals(CmbSignalInfo, "signal")
self.data = self.__init_data()
self.internal_children = self.__init_internal_children()
self.child_constraint, self.child_type_shortcuts = self.__init_child_constraint()
if self.parent_id == "enum":
@ -144,7 +155,7 @@ class CmbTypeInfo(CmbBaseTypeInfo):
c = self.project.db.cursor()
for row in c.execute(f"SELECT * FROM {table} WHERE owner_id=? ORDER BY {table}_id;", (self.type_id,)):
retval[row[1]] = Klass.from_row(self, *row)
retval[row[1]] = Klass.from_row(self.project, *row)
c.close()
return retval
@ -153,14 +164,14 @@ class CmbTypeInfo(CmbBaseTypeInfo):
args = {}
children = {}
parent_id = parent_id if parent_id is not None else 0
retval = CmbTypeDataInfo.from_row(self, owner_id, data_id, parent_id, key, type_id, translatable)
retval = CmbTypeDataInfo.from_row(self.project, owner_id, data_id, parent_id, key, type_id, translatable)
c = self.project.db.cursor()
# Collect Arguments
for row in c.execute("SELECT * FROM type_data_arg WHERE owner_id=? AND data_id=?;", (owner_id, data_id)):
_key = row[2]
args[_key] = CmbTypeDataArgInfo.from_row(self, *row)
args[_key] = CmbTypeDataArgInfo.from_row(self.project, *row)
# Recurse children
for row in c.execute("SELECT * FROM type_data WHERE owner_id=? AND parent_id=?;", (owner_id, data_id)):
@ -187,6 +198,52 @@ class CmbTypeInfo(CmbBaseTypeInfo):
c.close()
return retval
def __type_get_internal_child(self, type_id, internal_child_id, internal_parent_id, internal_type, creation_property_id):
retval = CmbTypeInternalChildInfo.from_row(
self.project,
type_id,
internal_child_id,
internal_parent_id,
internal_type,
creation_property_id
)
children = {}
c = self.project.db.cursor()
# Recurse children
for row in c.execute(
"SELECT * FROM type_internal_child WHERE type_id=? AND internal_parent_id=?;",
(type_id, internal_child_id)
):
key = row[1]
children[key] = self.__type_get_internal_child(*row)
c.close()
retval.children = children
# Internal child back reference in property
if creation_property_id:
if creation_property_id in self.properties:
self.properties[creation_property_id].internal_child = retval
return retval
def __init_internal_children(self):
retval = {}
c = self.project.db.cursor()
for row in c.execute(
"SELECT * FROM type_internal_child WHERE type_id=? AND internal_parent_id IS NULL ORDER BY internal_child_id;",
(self.type_id,)
):
key = row[1]
retval[key] = self.__type_get_internal_child(*row)
c.close()
return retval
def __init_child_constraint(self):
retval = {}
shortcuts = []
@ -274,7 +331,7 @@ class CmbTypeInfo(CmbBaseTypeInfo):
return False
def enum_get_value_as_string(self, value):
def enum_get_value_as_string(self, value, use_nick=True):
if self.parent_id != "enum":
return None
@ -283,7 +340,7 @@ class CmbTypeInfo(CmbBaseTypeInfo):
# Always use nick as value
if value == enum_name or value == enum_nick or value == str(enum_value):
return enum_nick
return enum_nick if use_nick else enum_value
return None

View File

@ -23,8 +23,10 @@
# SPDX-License-Identifier: LGPL-2.1-only
#
import os
from gi.repository import GObject, Gio
from .cmb_path import CmbPath
from .cmb_list_error import CmbListError
from .cmb_objects_base import CmbBaseUI, CmbBaseObject
from cambalache import getLogger, _
@ -37,6 +39,8 @@ class CmbUI(CmbBaseUI, Gio.ListModel):
"library-changed": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
}
path_parent = GObject.Property(type=CmbPath, flags=GObject.ParamFlags.READWRITE)
def __init__(self, **kwargs):
super().__init__(**kwargs)
@ -61,6 +65,10 @@ class CmbUI(CmbBaseUI, Gio.ListModel):
def __on_notify(self, obj, pspec):
self.project._ui_changed(self, pspec.name)
# Update display name if one of the following properties changed
if pspec.name in ["filename", "template-id"]:
self.notify("display-name")
def list_libraries(self):
retval = {}
@ -124,19 +132,21 @@ class CmbUI(CmbBaseUI, Gio.ListModel):
c.close()
@classmethod
def get_display_name(cls, ui_id, filename):
return os.path.basename(filename) if filename else _("Unnamed {ui_id}").format(ui_id=ui_id)
@GObject.Property(type=str)
def display_name(self):
if self.filename:
return self.filename
filename = self.filename
template_id = self.template_id
if template_id:
if filename is None and template_id:
template = self.project.get_object_by_id(self.ui_id, template_id)
if template:
return template.name
return _("Unnamed {ui_id}").format(ui_id=self.ui_id)
return CmbUI.get_display_name(self.ui_id, filename)
def __get_infered_target(self, library_id):
ui_id = self.ui_id
@ -177,9 +187,6 @@ class CmbUI(CmbBaseUI, Gio.ListModel):
return target
def export(self):
return self.project.export_ui(self)
# GListModel iface
def do_get_item(self, position):
ui_id = self.ui_id

View File

@ -23,8 +23,11 @@
# SPDX-License-Identifier: LGPL-2.1-only
#
import os
from gi.repository import GObject, Gtk
from cambalache import _
from .cmb_ui import CmbUI
@ -32,12 +35,8 @@ from .cmb_ui import CmbUI
class CmbUIEditor(Gtk.Grid):
__gtype_name__ = "CmbUIEditor"
__gsignals__ = {
"remove-ui": (GObject.SignalFlags.RUN_FIRST, None, (CmbUI,)),
"export-ui": (GObject.SignalFlags.RUN_FIRST, None, (CmbUI,)),
}
filename = Gtk.Template.Child()
format = Gtk.Template.Child()
template_id = Gtk.Template.Child()
description = Gtk.Template.Child()
copyright = Gtk.Template.Child()
@ -59,9 +58,6 @@ class CmbUIEditor(Gtk.Grid):
@object.setter
def _set_object(self, obj):
if obj == self._object:
return
for binding in self._bindings:
binding.unbind()
@ -82,10 +78,17 @@ class CmbUIEditor(Gtk.Grid):
self.set_sensitive(True)
self.template_id.object = obj
self.filename.dirname = obj.project.dirname
# Set some default name
self.filename.unnamed_filename = _("unnamed.ui")
if not obj.filename and obj.template_id:
template = obj.project.get_object_by_id(obj.ui_id, obj.template_id)
if template:
self.filename.unnamed_filename = f"{template.name}.ui".lower()
for field in self.fields:
binding = GObject.Object.bind_property(
obj,
binding = obj.bind_property(
field,
getattr(self, field),
"cmb-value",
@ -93,15 +96,42 @@ class CmbUIEditor(Gtk.Grid):
)
self._bindings.append(binding)
@Gtk.Template.Callback("on_remove_button_clicked")
def __on_remove_button_clicked(self, button):
if self.object:
self.emit("remove-ui", self.object)
if obj.project.target_tk == "gtk-4.0":
self.filename.mime_types = "application/x-gtk-builder;text/x-blueprint"
@Gtk.Template.Callback("on_export_button_clicked")
def __on_export_button_clicked(self, button):
if self.object:
self.emit("export-ui", self.object)
# filename -> format
binding = obj.bind_property(
"filename",
self.format,
"selected",
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
transform_to=self.__filename_to_format,
transform_from=self.__format_to_filename,
user_data=obj
)
self._bindings.append(binding)
self.format.show()
self.format.set_sensitive(bool(obj.filename))
else:
self.filename.mime_types = "application/x-gtk-builder;application/x-glade"
self.format.hide()
def __filename_to_format(self, binding, source_value, ui):
if not source_value:
self.format.props.sensitive = False
return 0
self.format.props.sensitive = True
return 1 if source_value.endswith(".blp") else 0
def __format_to_filename(self, binding, target_value, ui):
if not ui.filename:
self.format.props.sensitive = False
return None
self.format.props.sensitive = True
return os.path.splitext(ui.filename)[0] + (".blp" if target_value == 1 else ".ui")
Gtk.WidgetClass.set_css_name(CmbUIEditor, "CmbUIEditor")

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.3 -->
<!-- Created with Cambalache 0.97.1 -->
<interface>
<!-- interface-name cmb_ui_editor.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->
@ -27,7 +27,7 @@
<property name="label" translatable="yes">Description:</property>
<layout>
<property name="column">0</property>
<property name="row">2</property>
<property name="row">3</property>
</layout>
</object>
</child>
@ -37,7 +37,7 @@
<property name="label" translatable="yes">Copyright:</property>
<layout>
<property name="column">0</property>
<property name="row">3</property>
<property name="row">4</property>
</layout>
</object>
</child>
@ -47,7 +47,7 @@
<property name="label" translatable="yes">Authors:</property>
<layout>
<property name="column">0</property>
<property name="row">4</property>
<property name="row">5</property>
</layout>
</object>
</child>
@ -57,16 +57,13 @@
<property name="label" translatable="yes">Domain:</property>
<layout>
<property name="column">0</property>
<property name="row">5</property>
<property name="row">6</property>
</layout>
</object>
</child>
<child>
<object class="CmbEntry" id="filename">
<property name="can-focus">True</property>
<object class="CmbFileButton" id="filename">
<property name="hexpand">True</property>
<property name="placeholder-text" translatable="yes">&lt;file name relative to project&gt;</property>
<property name="visible">True</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
@ -81,7 +78,7 @@
<property name="visible">True</property>
<layout>
<property name="column">1</property>
<property name="row">5</property>
<property name="row">6</property>
</layout>
</object>
</child>
@ -98,7 +95,7 @@
<property name="min-content-height">96</property>
<layout>
<property name="column">1</property>
<property name="row">2</property>
<property name="row">3</property>
</layout>
</object>
</child>
@ -115,7 +112,7 @@
<property name="min-content-height">96</property>
<layout>
<property name="column">1</property>
<property name="row">4</property>
<property name="row">5</property>
</layout>
</object>
</child>
@ -127,7 +124,7 @@
<property name="visible">True</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
<property name="row">2</property>
</layout>
</object>
</child>
@ -137,45 +134,7 @@
<property name="label" translatable="yes">Template:</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkBox">
<property name="spacing">4</property>
<property name="valign">end</property>
<property name="vexpand">1</property>
<child>
<object class="GtkButton" id="remove_button">
<property name="focusable">1</property>
<property name="receives-default">1</property>
<signal name="clicked" handler="on_remove_button_clicked"/>
<child>
<object class="GtkImage">
<property name="icon-name">user-trash-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="export_button">
<property name="focusable">1</property>
<property name="halign">end</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Export</property>
<property name="receives-default">1</property>
<property name="tooltip-text" translatable="yes">Export</property>
<signal name="clicked" handler="on_export_button_clicked"/>
<style>
<class name="suggested-action"/>
</style>
</object>
</child>
<layout>
<property name="column">0</property>
<property name="column-span">2</property>
<property name="row">7</property>
<property name="row">2</property>
</layout>
</object>
</child>
@ -192,7 +151,7 @@
<property name="min-content-height">96</property>
<layout>
<property name="column">1</property>
<property name="row">3</property>
<property name="row">4</property>
</layout>
</object>
</child>
@ -202,7 +161,7 @@
<property name="label" translatable="yes">Comment:</property>
<layout>
<property name="column">0</property>
<property name="row">6</property>
<property name="row">7</property>
</layout>
</object>
</child>
@ -219,7 +178,34 @@
<property name="min-content-height">96</property>
<layout>
<property name="column">1</property>
<property name="row">6</property>
<property name="row">7</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Format:</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkDropDown" id="format">
<property name="halign">start</property>
<property name="model">
<object class="GtkStringList">
<property name="strings">Gtk Builder
Blueprint</property>
</object>
</property>
<layout>
<property name="column">1</property>
<property name="column-span">1</property>
<property name="row">1</property>
<property name="row-span">1</property>
</layout>
</object>
</child>

View File

@ -0,0 +1,57 @@
#
# CmbVersionNotificationView
#
# Copyright (C) 2025 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
from cambalache import _, getLogger
from gi.repository import GObject, Gtk
from .cmb_notification import CmbVersionNotification
logger = getLogger(__name__)
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_version_notification_view.ui")
class CmbVersionNotificationView(Gtk.Box):
__gtype_name__ = "CmbVersionNotificationView"
notification = GObject.Property(
type=CmbVersionNotification, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY
)
# Version
version_label = Gtk.Template.Child()
release_notes_label = Gtk.Template.Child()
read_more_button = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
notification = self.notification
self.version_label.props.label = _("<b>Version {version} is available</b>").format(version=notification.version)
self.release_notes_label.props.label = notification.release_notes
if notification.read_more_url:
self.read_more_button.props.uri = notification.read_more_url
self.read_more_button.show()
else:
self.read_more_button.hide()

View File

@ -0,0 +1,50 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_version_notification_view.ui -->
<requires lib="gtk" version="4.12"/>
<template class="CmbVersionNotificationView" parent="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="version_label">
<property name="halign">start</property>
<property name="use-markup">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="release_notes_label">
<property name="halign">start</property>
<property name="lines">16</property>
<property name="max-width-chars">32</property>
<property name="use-markup">True</property>
<property name="valign">start</property>
<property name="vexpand">True</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="spacing">4</property>
<property name="valign">end</property>
<child>
<object class="GtkLinkButton" id="read_more_button">
<property name="label" translatable="yes">Read more...</property>
<property name="uri">https://blogs.gnome.org/xjuan/</property>
<style>
<class name="compact"/>
</style>
</object>
</child>
<child>
<object class="GtkLinkButton" id="download_button">
<property name="label" translatable="yes">Get it on Flathub</property>
<property name="uri">https://flathub.org/apps/ar.xjuan.Cambalache</property>
<style>
<class name="compact"/>
</style>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -39,8 +39,9 @@ from . import config
from .cmb_ui import CmbUI
from .cmb_object import CmbObject
from .cmb_context_menu import CmbContextMenu
from cambalache.cmb_blueprint import cmb_blueprint_decompile
from . import utils
from cambalache import getLogger, _
from cambalache import getLogger, _, N_
logger = getLogger(__name__)
@ -167,7 +168,6 @@ class CmbMerengueProcess(GObject.Object):
env = json.loads(os.environ.get("MERENGUE_DEV_ENV", "{}"))
env = env | {
"GDK_BACKEND": "wayland",
"GSK_RENDERER": "cairo",
"WAYLAND_DISPLAY": self.wayland_display,
}
@ -219,6 +219,7 @@ class CmbMerengueProcess(GObject.Object):
# Encode to binary first, before calculating lenght
payload = payload.encode()
cmd["payload_length"] = len(payload)
logger.debug(f"write_command {command} {len(payload)}")
if args is not None:
cmd["args"] = args
@ -233,11 +234,18 @@ class CmbMerengueProcess(GObject.Object):
def __socket_write_command(self, cmd, payload=None):
# Send command in one line as json
output_stream = self.__connection.props.output_stream
output_stream.write(json.dumps(cmd).encode())
output_stream.write(b"\n")
def write_data(data):
total_bytes = len(data)
total_sent = 0
while total_sent < total_bytes:
total_sent += output_stream.write(data[total_sent:])
write_data(json.dumps(cmd).encode())
write_data(b"\n")
if payload is not None:
output_stream.write(payload)
write_data(payload)
# Flush
output_stream.flush()
@ -271,9 +279,8 @@ class CmbView(Gtk.Box):
def __init__(self, **kwargs):
self.__project = None
self.__ui_id = 0
self.__ui = None
self.__theme = None
self.__dark = False
self.menu = self.__create_context_menu()
@ -308,12 +315,17 @@ class CmbView(Gtk.Box):
if os.path.exists(dirname):
shutil.rmtree(dirname)
def _set_dark_mode(self, dark):
self.__dark = dark
bg_color = Gdk.RGBA()
bg_color.parse("gray18" if dark else "white")
def __set_dark_mode(self, dark):
valid, bg_color = self.get_style_context().lookup_color('theme_bg_color')
if valid:
self.compositor.props.bg_color = bg_color
return GLib.SOURCE_REMOVE
def _set_dark_mode(self, dark):
# This needs to be called in an idle because theme_bg_color has not changed at this point
GLib.idle_add(self.__set_dark_mode, dark)
def __merengue_command(self, command, payload=None, args=None):
if self.__merengue.merengue_started:
self.__merengue.write_command(command, payload, args)
@ -325,14 +337,34 @@ class CmbView(Gtk.Box):
return self.__project.db.tostring(ui_id, merengue=merengue)
def __update_view(self):
if self.__project and self.__ui_id > 0:
if self.__project and self.__ui:
if self.stack.props.visible_child_name == "ui_xml":
ui = self.__get_ui_xml(self.__ui_id)
self.text_view.buffer.set_text(ui)
ui_source = self.__get_ui_xml(self.__ui.ui_id)
if self.__ui.filename and self.__ui.filename.endswith(".blp"):
try:
ui_source = cmb_blueprint_decompile(ui_source)
self.text_view.lang = "blueprint"
except Exception as e:
ui_source = _("Error exporting project")
ui_source += "\n"
ui_source += N_(
"blueprintcompiler encounter the following error:",
"blueprintcompiler encounter the following errors:",
len(e.errors)
)
ui_source += "\n"
ui_source += str(e)
self.text_view.lang = ""
# TODO: forward error to parent to show to user
else:
self.text_view.lang = "xml"
self.text_view.buffer.set_text(ui_source)
return
self.text_view.buffer.set_text("")
self.__ui_id = 0
self.__ui = None
def __get_ui_dirname(self, ui_id):
dirname = GLib.get_home_dir()
@ -436,19 +468,23 @@ class CmbView(Gtk.Box):
if len(selection) > 0:
obj = selection[0]
if type(obj) not in [CmbUI, CmbObject]:
if isinstance(obj, CmbUI):
ui = obj
elif isinstance(obj, CmbObject):
ui = obj.ui
else:
return
ui_id = obj.ui_id
if self.__ui_id != ui_id:
self.__ui_id = ui_id
self.__merengue_update_ui(ui_id)
if self.__ui != ui:
self.__ui = ui
self.__merengue_update_ui(ui.ui_id)
objects = self.__get_selection_objects(selection, ui_id)
objects = self.__get_selection_objects(selection, ui.ui_id)
self.__merengue_command("selection_changed", args={"ui_id": ui_id, "selection": objects})
else:
self.__ui_id = 0
self.__ui = None
self.__merengue_update_ui(0)
self.__update_view()
@ -620,7 +656,7 @@ class CmbView(Gtk.Box):
self.__merengue_last_exit = None
return
self.__ui_id = 0
self.__ui = None
self.__merengue.start()
def __command_selection_changed(self, selection):
@ -679,7 +715,7 @@ class CmbView(Gtk.Box):
self.__load_css_providers()
self.__ui_id = 0
self.__ui = None
self.__on_project_selection_changed(self.__project)
elif command == "placeholder_selected":
self.emit(

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.3 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_view.ui -->
<!-- interface-copyright Juan Pablo Ugarte -->

View File

@ -25,6 +25,7 @@
from .cmb_boolean_undefined import CmbBooleanUndefined
from .cmb_child_type_combo_box import CmbChildTypeComboBox
from .cmb_entry import CmbEntry
from .cmb_file_button import CmbFileButton
from .cmb_file_entry import CmbFileEntry
from .cmb_icon_name_entry import CmbIconNameEntry
from .cmb_pixbuf_entry import CmbPixbufEntry

View File

@ -0,0 +1,114 @@
#
# CmbFileButton
#
# Copyright (C) 2021-2023 Juan Pablo Ugarte
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
#
# library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Authors:
# Juan Pablo Ugarte <juanpablougarte@gmail.com>
#
# SPDX-License-Identifier: LGPL-2.1-only
#
import os
from cambalache import _
from gi.repository import GObject, Gio, Gtk
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/control/cmb_file_button.ui")
class CmbFileButton(Gtk.Button):
__gtype_name__ = "CmbFileButton"
dirname = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
dialog_title = GObject.Property(type=str, default=_("Select filename"), flags=GObject.ParamFlags.READWRITE)
accept_label = GObject.Property(type=str, default=_("Select"), flags=GObject.ParamFlags.READWRITE)
unnamed_filename = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
use_open = GObject.Property(type=bool, default=False, flags=GObject.ParamFlags.READWRITE)
label = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.__filename = None
self.__filters = None
@Gtk.Template.Callback("on_button_clicked")
def __on_button_clicked(self, button):
dialog = Gtk.FileDialog(
modal=True,
filters=self.__filters,
title=self.dialog_title,
accept_label=self.accept_label
)
if self.dirname is not None:
if self.__filename:
fullpath = os.path.join(self.dirname, self.__filename)
file = Gio.File.new_for_path(fullpath)
dialog.set_initial_file(file)
# See which filter matches the file info and use it as default
if file.query_exists(None):
info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, Gio.FileQueryInfoFlags.NONE, None)
for filter in self.__filters:
if filter.match(info):
dialog.set_default_filter(filter)
break
else:
dialog.set_initial_folder(Gio.File.new_for_path(self.dirname))
if self.unnamed_filename:
dialog.set_initial_name(self.unnamed_filename)
def dialog_callback(dialog, res):
try:
file = dialog.open_finish(res) if self.use_open else dialog.save_finish(res)
self.cmb_value = os.path.relpath(file.get_path(), start=self.dirname)
except Exception:
pass
if self.use_open:
dialog.open(self.get_root(), None, dialog_callback)
else:
dialog.save(self.get_root(), None, dialog_callback)
@GObject.Property(type=str)
def cmb_value(self):
return self.__filename
@cmb_value.setter
def _set_cmb_value(self, value):
if value == self.__filename:
return
self.__filename = value if value is not None else ""
self.label.set_label(self.__filename)
@GObject.Property(type=str)
def mime_types(self):
if self.__filters:
return ";".join([f.props.mime_types for f in self.__filters])
return ""
@mime_types.setter
def _set_mime_types(self, value):
if value:
self.__filters = Gio.ListStore()
for mime in value.split(';'):
self.__filters.append(Gtk.FileFilter(mime_types=[mime]))
else:
self.__filters = None

View File

@ -0,0 +1,34 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_file_button.ui -->
<requires lib="gtk" version="4.12"/>
<template class="CmbFileButton" parent="GtkButton">
<signal name="clicked" handler="on_button_clicked"/>
<child>
<object class="GtkBox">
<property name="spacing">4</property>
<child>
<object class="GtkImage" id="file_icon">
<property name="visible">False</property>
</object>
</child>
<child>
<object class="GtkLabel" id="label">
<property name="ellipsize">start</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label">(None)</property>
<property name="width-chars">8</property>
<property name="xalign">0.0</property>
</object>
</child>
<child>
<object class="GtkImage">
<property name="icon-name">folder-open-symbolic</property>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -34,6 +34,7 @@ class CmbFlagsEntry(Gtk.Entry):
id_column = GObject.Property(type=int, default=1, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
text_column = GObject.Property(type=int, default=1, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
value_column = GObject.Property(type=int, default=2, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
separator = GObject.Property(type=str, default="|", flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY)
def __init__(self, **kwargs):
self.flags = {}
@ -84,7 +85,7 @@ class CmbFlagsEntry(Gtk.Entry):
for row in self.info.flags:
flag_id = row[self.id_column]
if self.flags.get(flag_id, False):
retval = flag_id if retval is None else f"{retval} | {flag_id}"
retval = flag_id if retval is None else f"{retval} {self.separator} {flag_id}"
return retval if retval is not None else ""
@ -104,7 +105,7 @@ class CmbFlagsEntry(Gtk.Entry):
self._checks[check].props.active = False
if value:
tokens = [t.strip() for t in value.split("|")]
tokens = [t.strip() for t in value.split(self.separator)]
for row in self.info.flags:
flag_id = row[self.id_column]

View File

@ -40,7 +40,8 @@ class CmbSourceView(GtkSource.View):
@GObject.Property(type=str)
def lang(self):
return self.buffer.get_language()
language = self.buffer.get_language()
return language.get_id() if language else ""
@lang.setter
def _set_lang(self, value):
@ -49,7 +50,7 @@ class CmbSourceView(GtkSource.View):
@GObject.Property(type=str)
def text(self):
return self.buffer.props.text
return self.buffer.props.text if len(self.buffer.props.text) else None
@text.setter
def _set_text(self, value):

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.91.1 -->
<!-- Created with Cambalache 0.95.0 -->
<interface>
<!-- interface-name cmb_translatable_widget.ui -->
<!-- interface-copyright Philipp Unger -->

View File

@ -4,22 +4,23 @@ moduledir = join_paths(modulesdir, 'cambalache', 'control')
install_data([
'cmb_boolean_undefined.py',
'cmb_child_type_combo_box.py',
'cmb_entry.py',
'cmb_file_entry.py',
'cmb_icon_name_entry.py',
'cmb_pixbuf_entry.py',
'cmb_spin_button.py',
'cmb_text_buffer.py',
'cmb_toplevel_chooser.py',
'cmb_color_entry.py',
'cmb_create_editor.py',
'cmb_entry.py',
'cmb_enum_combo_box.py',
'cmb_file_button.py',
'cmb_file_entry.py',
'cmb_flags_entry.py',
'cmb_icon_name_entry.py',
'cmb_object_chooser.py',
'cmb_object_list_editor.py',
'cmb_pixbuf_entry.py',
'cmb_source_view.py',
'cmb_spin_button.py',
'cmb_switch.py',
'cmb_text_buffer.py',
'cmb_text_view.py',
'cmb_toplevel_chooser.py',
'cmb_translatable_popover.py',
'cmb_translatable_widget.py',
'icon_naming_spec.py',

View File

@ -170,29 +170,19 @@ CREATE TABLE IF NOT EXISTS type_child_constraint (
) WITHOUT ROWID;
/* Type Tree
/* Type Internal Child
*
* VIEW of ancestors and ifaces by type
*/
CREATE VIEW IF NOT EXISTS type_tree AS
WITH RECURSIVE ancestor(type_id, generation, parent_id) AS (
SELECT type_id, 1, parent_id FROM type
WHERE parent_id IS NOT NULL AND
parent_id != 'interface' AND
parent_id != 'enum' AND
parent_id != 'flags'
UNION ALL
SELECT ancestor.type_id, generation + 1, type.parent_id
FROM type JOIN ancestor ON type.type_id = ancestor.parent_id
WHERE type.parent_id IS NOT NULL
)
SELECT * FROM ancestor
UNION
SELECT ancestor.type_id, 0, type_iface.iface_id
FROM ancestor JOIN type_iface
WHERE ancestor.type_id = type_iface.type_id
ORDER BY type_id,generation;
CREATE TABLE IF NOT EXISTS type_internal_child (
type_id TEXT REFERENCES type ON UPDATE CASCADE,
internal_child_id TEXT,
internal_parent_id TEXT,
internal_type TEXT REFERENCES type ON UPDATE CASCADE,
creation_property_id TEXT,
PRIMARY KEY(type_id, internal_child_id),
FOREIGN KEY(type_id, internal_parent_id) REFERENCES type_internal_child(type_id, internal_child_id)
FOREIGN KEY(type_id, creation_property_id) REFERENCES property(owner_id, property_id)
) WITHOUT ROWID;
/* Property
@ -213,7 +203,7 @@ CREATE TABLE IF NOT EXISTS property (
deprecated_version TEXT,
translatable BOOLEAN CHECK (type_id IN (NULL, 'gchararray')),
disable_inline_object BOOLEAN DEFAULT 0,
is_position BOOLEAN DEFAULT 0,
deprecated TEXT,
required BOOLEAN DEFAULT 0,
workspace_default TEXT,
original_owner_id TEXT REFERENCES type ON UPDATE CASCADE,

View File

@ -113,6 +113,26 @@ CREATE TABLE css_ui (
) WITHOUT ROWID;
/* GResource
*
*/
CREATE TABLE gresource (
gresource_id INTEGER PRIMARY KEY AUTOINCREMENT,
resource_type TEXT CHECK (resource_type IN ('gresources', 'gresource', 'file')),
parent_id INTEGER REFERENCES gresource ON DELETE CASCADE,
position INTEGER NOT NULL CHECK (position >= 0),
gresources_filename TEXT UNIQUE,
gresource_prefix TEXT UNIQUE,
file_filename TEXT,
file_compressed BOOLEAN,
file_preprocess TEXT,
file_alias TEXT
);
/* Object
*
* TODO: check type_id is an object

View File

@ -23,14 +23,22 @@
#
import os
import gi
import logging
gi.require_version('GIRepository', '3.0')
from . import config
from gi.repository import Gio
from gi.repository import Gio, Gtk
resource = Gio.Resource.load(os.path.join(config.pkgdatadir, "merengue.gresource"))
resource._register()
repository = gi.Repository.get_default()
repository.prepend_search_path(config.privatecambalachedir)
repository.prepend_library_path(config.privatecambalachedir)
gi.require_version("CambalachePrivate", "4.0" if Gtk.MAJOR_VERSION == 4 else "3.0")
def getLogger(name):
formatter = logging.Formatter("%(levelname)s:%(name)s %(message)s")
@ -49,3 +57,4 @@ from .mrg_application import MrgApplication
from .mrg_controller import MrgController
from .mrg_placeholder import MrgPlaceholder
from . import utils

View File

@ -1,3 +1,4 @@
VERSION = '@VERSION@'
pkgdatadir = '@pkgdatadir@'
merenguedir = '@merenguedir@'
privatecambalachedir = '@privatecambalachedir@'

View File

@ -29,7 +29,7 @@ import gi
import sys
import signal
merenguedir = '@merenguedir@'
merenguedir = "@merenguedir@"
sys.path.insert(1, merenguedir)
signal.signal(signal.SIGINT, signal.SIG_DFL)
@ -43,9 +43,9 @@ if __name__ == "__main__":
version = sys.argv[1]
command_socket = sys.argv[2]
gi.require_version('Gdk', version)
gi.require_version('Gtk', version)
gi.require_version('CambalachePrivate', version)
gi.require_version("Gdk", version)
gi.require_version("Gtk", version)
from merengue import MrgApplication
app = MrgApplication(command_socket=command_socket)

View File

@ -12,6 +12,7 @@ conf.set('VERSION', meson.project_version())
conf.set('PYTHON', python_bin.full_path())
conf.set('pkgdatadir', pkgdatadir)
conf.set('merenguedir', merenguedir)
conf.set('privatecambalachedir', privatecambalachedir)
configure_file(
input: 'config.py.in',

View File

@ -25,7 +25,6 @@
import os
import gi
import sys
import json
import importlib
@ -80,10 +79,6 @@ class MrgApplication(Gtk.Application):
# Current UI ID
self.ui_id = None
# The widget that got the last button press this is used to select
# a widget on button release
self.preselected_widget = None
self.settings = Gtk.Settings.get_default()
# Keep a reference to the default seat to easily ungrab the pointer
@ -125,7 +120,6 @@ class MrgApplication(Gtk.Application):
def clear_all(self):
self.ui_id = None
self.preselected_widget = None
# Unset controllers objects
for key in self.controllers:
@ -304,7 +298,13 @@ class MrgApplication(Gtk.Application):
self.set_property(property, value)
def add_css_provider(self, css_id, filename, priority, is_global, provider_for):
css = MrgCssProvider(filename=filename, priority=priority, is_global=is_global, ui_id=self.ui_id if self.ui_id else 0)
css = MrgCssProvider(
filename=filename,
priority=priority,
is_global=is_global,
provider_for=provider_for,
ui_id=self.ui_id if self.ui_id else 0
)
self.css_providers[css_id] = css
@ -327,7 +327,7 @@ class MrgApplication(Gtk.Application):
css.set_property(field, value)
def run_command(self, command, args, payload):
logger.debug(f"{command} {args}")
logger.debug(f"{command} {args} payload={len(payload) if payload else -1}")
if command == "clear_all":
self.clear_all()
@ -388,7 +388,9 @@ class MrgApplication(Gtk.Application):
# Read payload
if payload_length:
payload = self.command_in.read(payload_length).decode()
payload = self.command_in.read(payload_length)
logger.debug(f"Payload read {payload_length=}, {len(payload)}")
payload = payload.decode()
# Run command
self.run_command(command, args, payload)

View File

@ -42,14 +42,15 @@ class MrgGtkLabel(MrgGtkWidget):
# Ensure a label so that it can be selected in the workspace
if self.object.props.label == "":
self.object.set_label("<label>")
label = f"&lt;label {self.object_id}&gt;" if self.object.props.use_markup else f"<label {self.object_id}>"
self.object.set_label(label)
def object_changed(self, old, new):
super().object_changed(old, new)
self.__init_label()
def set_object_property(self, name, value):
if name == "label" and value == "":
if (name == "label" and value == "") or name == "use-markup":
self.__init_label()
return

View File

@ -22,7 +22,7 @@
# SPDX-License-Identifier: LGPL-2.1-only
#
from gi.repository import GObject, Gdk, Gtk
from gi.repository import GObject, Gdk, Gtk, CambalachePrivate
from .mrg_selection import MrgSelection
from .mrg_gtk_widget import MrgGtkWidget
@ -43,7 +43,7 @@ class MrgGtkMenu(MrgGtkWidget):
if self.object is None:
return
self.object.popup_at_widget(self.__button, Gdk.Gravity.SOUTH_WEST, Gdk.Gravity.NORTH_WEST, None)
self.object.popup_at_widget(self.object.get_attach_widget(), Gdk.Gravity.SOUTH_WEST, Gdk.Gravity.NORTH_WEST, None)
def object_changed(self, old, new):
self.selection = None
@ -54,6 +54,10 @@ class MrgGtkMenu(MrgGtkWidget):
self.window = None
return
self.selection = MrgSelection(app=self.app, container=self.object)
if self.object.get_attach_widget() is not None:
return
if self.window is None:
self.__button = Gtk.MenuButton(
visible=True, halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, receives_default=False
@ -63,21 +67,19 @@ class MrgGtkMenu(MrgGtkWidget):
self.__button.add(image)
self.window = Gtk.Window(title="Menu Preview Window", deletable=False, width_request=320, height_request=240)
self.window.set_default_size(640, 480)
self.window.add(self.__button)
self.selection = MrgSelection(app=self.app, container=self.object)
self.__button.set_popup(self.object)
self.object.show_all()
self.window.show_all()
CambalachePrivate.widget_set_application_id(self.window, f"Casilda:{self.ui_id}.{self.object_id}")
def on_selected_changed(self):
super().on_selected_changed()
if self.__button:
self.__button.set_active(True)
if self.selected:
self.popup()
else:
self.object.popdown()
def show_child(self, child):
if self.__button:
self.__button.set_active(True)
self.popup()

View File

@ -26,8 +26,6 @@ from gi.repository import GObject, Gtk
from merengue import utils, MrgPlaceholder
preselected_widget = None
class FindInContainerData:
def __init__(self, toplevel, x, y):
@ -42,6 +40,7 @@ class MrgSelection(GObject.GObject):
app = GObject.Property(type=GObject.GObject, flags=GObject.ParamFlags.READWRITE)
def __init__(self, **kwargs):
self.__selecting = False
self._container = None
self.gesture = None
@ -59,29 +58,14 @@ class MrgSelection(GObject.GObject):
self.gesture = utils.gesture_click_new(self._container, propagation_phase=Gtk.PropagationPhase.CAPTURE)
self.gesture.connect("pressed", self.__on_gesture_button_pressed)
self.gesture.connect("released", self.__on_gesture_button_released)
else:
elif self.gesture:
self.gesture.disconnect_by_func(self.__on_gesture_button_pressed)
self.gesture.disconnect_by_func(self.__on_gesture_button_released)
self.gesture = None
def __on_gesture_button_pressed(self, gesture, n_press, x, y):
global preselected_widget
child = self.get_child_at_position(self._container, x, y)
if not self.is_widget_from_ui(child):
return
# Pre select a widget on button press
if preselected_widget != child:
preselected_widget = child
gesture.set_state(Gtk.EventSequenceState.CLAIMED)
def __on_gesture_button_released(self, gesture, n_press, x, y):
global preselected_widget
child = self.get_child_at_position(self._container, x, y)
if child != preselected_widget:
return
if isinstance(child, MrgPlaceholder):
controller = child.controller
@ -103,7 +87,15 @@ class MrgSelection(GObject.GObject):
# Select widget on button release only if its preselected
self.app.write_command("selection_changed", args={"selection": [object_id]})
controller.selected = True
if not isinstance(child, Gtk.Window) and not isinstance(child, Gtk.HeaderBar):
gesture.set_state(Gtk.EventSequenceState.CLAIMED)
self.__selecting = True
def __on_gesture_button_released(self, gesture, n_press, x, y):
if self.__selecting:
gesture.set_state(Gtk.EventSequenceState.CLAIMED)
self.__selecting = False
def is_widget_from_ui(self, obj):
if isinstance(obj, MrgPlaceholder):
@ -157,3 +149,4 @@ class MrgSelection(GObject.GObject):
return widget
return None

View File

@ -26,7 +26,7 @@ configure_file(
install_data([
'cmb_accessible_editor.py',
'cmb_base.py',
'cmb_column_view.py',
'cmb_blueprint.py',
'cmb_context_menu.py',
'cmb_css.py',
'cmb_css_editor.py',
@ -35,20 +35,30 @@ install_data([
'cmb_db_migration.py',
'cmb_db_profile.py',
'cmb_fragment_editor.py',
'cmb_gresource.py',
'cmb_gresource_editor.py',
'cmb_layout_property.py',
'cmb_library_info.py',
'cmb_list_error.py',
'cmb_list_store.py',
'cmb_list_view.py',
'cmb_message_notification_view.py',
'cmb_notification.py',
'cmb_notification_list_row.py',
'cmb_notification_list_view.py',
'cmb_object.py',
'cmb_object_data.py',
'cmb_object_data_editor.py',
'cmb_object_editor.py',
'cmb_objects_base.py',
'cmb_path.py',
'cmb_poll_notification_view.py',
'cmb_poll_option_check.py',
'cmb_project.py',
'cmb_property.py',
'cmb_property_info.py',
'cmb_property_label.py',
'cmb_signal_editor.py',
'cmb_tree_expander.py',
'cmb_type_chooser.py',
'cmb_type_chooser_popover.py',
'cmb_type_chooser_widget.py',
@ -56,6 +66,7 @@ install_data([
'cmb_ui.py',
'cmb_ui_editor.py',
'cmb_ui_requires_editor.py',
'cmb_version_notification_view.py',
'cmb_view.py',
'constants.py',
'utils.py',

View File

@ -15,6 +15,7 @@ foreach d : [['3', gtk3_dep], ['4', gtk4_dep]]
sources,
dependencies: dep,
install: true,
install_dir: privatecambalachedir,
)
gnome.generate_gir(
@ -27,5 +28,6 @@ foreach d : [['3', gtk3_dep], ['4', gtk4_dep]]
symbol_prefix: 'cmb_private',
header: 'cmb_private.h',
install: true,
install_dir_typelib: privatecambalachedir,
)
endforeach

View File

@ -23,6 +23,10 @@
# SPDX-License-Identifier: LGPL-2.1-only
#
import hashlib
import datetime
from lxml import etree
from gi.repository import Gdk, Gio
@ -99,3 +103,101 @@ def content_type_guess(path):
content_type, uncertain = Gio.content_type_guess(path, data)
return content_type
def utcnow():
return int(datetime.datetime.now(datetime.UTC).timestamp())
# XML utilities
def bool_from_string(value):
if isinstance(value, str):
return value.lower() in {"1", "t", "y", "true", "yes"} if value else False
return bool(value)
def xml_node_get(node, *args, errors=None):
keys = node.keys()
knowns = []
retval = []
def get_key_val(node, attr):
tokens = attr.split(":")
key = tokens[0]
val = node.get(key, None)
if len(tokens) > 1:
t = tokens[1]
if t == "bool":
return (key, bool_from_string(val))
elif t == "int":
return (key, int(val))
return (key, val)
for attr in args:
if isinstance(attr, list):
for opt in attr:
key, val = get_key_val(node, opt)
retval.append(val)
knowns.append(key)
elif attr in keys:
key, val = get_key_val(node, attr)
retval.append(val)
knowns.append(key)
elif errors is not None:
errors.append(("missing-attr", node, attr))
if errors is not None:
unknown = list(set(keys) - set(knowns))
for attr in unknown:
errors.append(("unknown-attr", node, attr))
return retval
def xml_node_get_comment(node):
prev = node.getprevious()
if prev is not None and prev.tag is etree.Comment:
return prev.text if not prev.text.strip().startswith("interface-") else None
return None
def xml_node_set(node, attr, val):
if val is not None:
node.set(attr, str(val))
# Duck typing Classes
class FileHash():
def __init__(self, fd=None):
self.__fd = fd
self.__hash = hashlib.sha256()
def close(self):
if self.__fd:
self.__fd.close()
def peek(self, size):
return self.__fd.peek(size) if self.__fd else None
def read(self, size):
if self.__fd:
data = self.__fd.read(size)
self.__hash.update(data)
return data
return None
def flush(self):
if self.__fd:
self.__fd.flush()
def write(self, data):
self.__hash.update(data)
if self.__fd:
self.__fd.write(data)
def hexdigest(self):
return self.__hash.hexdigest()

View File

@ -121,7 +121,7 @@
("AtkRole","ATK_ROLE_INVALID","invalid",None,"Invalid role"),
("AtkRole","ATK_ROLE_LABEL","label",28,"An object used to present an icon or short string in an interface"),
("AtkRole","ATK_ROLE_LANDMARK","landmark",108,"A region of a web page intended as a navigational landmark. This is designed to allow Assistive Technologies to provide quick navigation among key regions within a document. (Since: 2.12)"),
("AtkRole","ATK_ROLE_LAST_DEFINED","last-defined",128,"not a valid role, used for finding end of the enumeration"),
("AtkRole","ATK_ROLE_LAST_DEFINED","last-defined",129,"not a valid role, used for finding end of the enumeration"),
("AtkRole","ATK_ROLE_LAYERED_PANE","layered-pane",29,"A specialized pane that allows its children to be drawn in layers, providing a form of stacking order"),
("AtkRole","ATK_ROLE_LEVEL_BAR","level-bar",101,"A bar that serves as a level indicator to, for instance, show the strength of a password or the state of a battery. (Since: 2.7.3)"),
("AtkRole","ATK_ROLE_LINK","link",86,"The object is a hypertext anchor, i.e. a \"link\" in a hypertext document. Such objects are distinct from 'inline' content which may also use the Hypertext/Hyperlink interfaces to indicate the range/location within a text object where an inline or embedded object lies. (Since: 1.12.1)"),
@ -168,6 +168,7 @@
("AtkRole","ATK_ROLE_SUBSCRIPT","subscript",120,"An object that contains text that is displayed as a subscript. (Since: 2.16)"),
("AtkRole","ATK_ROLE_SUGGESTION","suggestion",126,"A container for content that is called out as a proposed change from the current version of the document, such as by a reviewer of the content. This role should include either %ATK_ROLE_CONTENT_DELETION and/or %ATK_ROLE_CONTENT_INSERTION children, in any order, to indicate what the actual change is. (Since: 2.36)"),
("AtkRole","ATK_ROLE_SUPERSCRIPT","superscript",121,"An object that contains text that is displayed as a superscript. (Since: 2.16)"),
("AtkRole","ATK_ROLE_SWITCH","switch",128,"A switch that can be toggled on/off. (Since: 2.56)"),
("AtkRole","ATK_ROLE_TABLE","table",54,"An object used to represent information in terms of rows and columns"),
("AtkRole","ATK_ROLE_TABLE_CELL","table-cell",55,"A cell in a table"),
("AtkRole","ATK_ROLE_TABLE_COLUMN_HEADER","table-column-header",56,"The header for a column of a table"),

View File

@ -382,8 +382,8 @@
("GAppInfoCreateFlags","G_APP_INFO_CREATE_SUPPORTS_URIS","supports-uris",2,"Application supports URI arguments."),
("GApplicationFlags","G_APPLICATION_ALLOW_REPLACEMENT","allow-replacement",128,"Allow another instance to take over the bus name. Since: 2.60"),
("GApplicationFlags","G_APPLICATION_CAN_OVERRIDE_APP_ID","can-override-app-id",64,"Allow users to override the application ID from the command line with `--gapplication-app-id`. Since: 2.48"),
("GApplicationFlags","G_APPLICATION_DEFAULT_FLAGS","default-flags",None,"Default flags. Since: 2.74"),
("GApplicationFlags","G_APPLICATION_FLAGS_NONE","flags-none",None,"Default. Deprecated in 2.74, use %G_APPLICATION_DEFAULT_FLAGS instead"),
("GApplicationFlags","G_APPLICATION_DEFAULT_FLAGS","default-flags",None,"Default flags."),
("GApplicationFlags","G_APPLICATION_FLAGS_NONE","flags-none",None,"Default flags."),
("GApplicationFlags","G_APPLICATION_HANDLES_COMMAND_LINE","handles-command-line",8,"This application handles command line arguments (in the primary instance). Note that this flag only affect the default implementation of local_command_line(). See g_application_run() for details."),
("GApplicationFlags","G_APPLICATION_HANDLES_OPEN","handles-open",4,"This application handles opening files (in the primary instance). Note that this flag only affects the default implementation of local_command_line(), and has no effect if %G_APPLICATION_HANDLES_COMMAND_LINE is given. See g_application_run() for details."),
("GApplicationFlags","G_APPLICATION_IS_LAUNCHER","is-launcher",2,"Don't try to become the primary instance."),

View File

@ -187,9 +187,17 @@
</properties>
</AdwPreferencesDialog>
<AdwWindow>
<properties>
<property original-owner-id="GtkWindow" id="titlebar" disabled="True"/>
<property original-owner-id="GtkWindow" id="child" disabled="True"/>
</properties>
</AdwWindow>
<AdwApplicationWindow>
<properties>
<property original-owner-id="GtkWindow" id="titlebar" disabled="True"/>
<property original-owner-id="GtkWindow" id="child" disabled="True"/>
</properties>
</AdwApplicationWindow>

View File

@ -34,12 +34,6 @@
</properties>
</HdyHeaderBar>
<HdyHeaderBarLayoutChild>
<properties>
<property id="position" is-position="True"/>
</properties>
</HdyHeaderBarLayoutChild>
<HdyPreferencesGroup>
<properties>
<property id="description" translatable="True"/>

View File

@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE cambalache-catalog SYSTEM "cambalache-catalog.dtd">
<cambalache-catalog name="libadwaita" namespace="Adw" prefix="Adw" version="1" targets="1.0,1.1,1.2,1.3,1.4,1.5,1.6" depends="gtk-4.0">
<cambalache-catalog name="libadwaita" namespace="Adw" prefix="Adw" version="1" targets="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7" depends="gtk-4.0">
<type>
("AdwAboutDialog","AdwDialog","libadwaita","1.5",None,None,1,"container","toplevel",None),
("AdwAboutWindow","AdwWindow","libadwaita","1.2","1.6",None,1,"container","toplevel",None),
@ -14,6 +14,7 @@
("AdwApplicationWindow","GtkApplicationWindow","libadwaita",None,None,None,1,"container","toplevel",None),
("AdwAvatar","GtkWidget","libadwaita",None,None,None,1,"container","display",None),
("AdwBanner","GtkWidget","libadwaita","1.3",None,None,1,"container","display",None),
("AdwBannerButtonStyle","enum","libadwaita",None,None,None,None,None,None,None),
("AdwBin","GtkWidget","libadwaita",None,None,None,1,"container","layout",None),
("AdwBottomSheet","GtkWidget","libadwaita","1.6",None,None,1,"container","layout",None),
("AdwBreakpoint","GObject","libadwaita","1.4",None,None,1,None,None,None),
@ -44,6 +45,9 @@
("AdwFlapTransitionType","enum","libadwaita",None,None,None,None,None,None,None),
("AdwFoldThresholdPolicy","enum","libadwaita",None,None,None,None,None,None,None),
("AdwHeaderBar","GtkWidget","libadwaita",None,None,None,1,"container","layout",None),
("AdwInlineViewSwitcher","GtkWidget","libadwaita","1.7",None,None,1,"container",None,None),
("AdwInlineViewSwitcherDisplayMode","enum","libadwaita",None,None,None,None,None,None,None),
("AdwJustifyMode","enum","libadwaita",None,None,None,None,None,None,None),
("AdwLayout","GObject","libadwaita","1.6",None,None,1,"container","layout",None),
("AdwLayoutSlot","GtkWidget","libadwaita","1.6",None,None,1,"container","layout",None),
("AdwLeaflet","GtkWidget","libadwaita",None,"1.4",None,1,"container","layout",None),
@ -57,6 +61,7 @@
("AdwNavigationSplitView","GtkWidget","libadwaita","1.4",None,None,1,"container","layout",None),
("AdwNavigationView","GtkWidget","libadwaita","1.4",None,None,1,"container","layout",None),
("AdwOverlaySplitView","GtkWidget","libadwaita","1.4",None,None,1,"container","layout",None),
("AdwPackDirection","enum","libadwaita",None,None,None,None,None,None,None),
("AdwPasswordEntryRow","AdwEntryRow","libadwaita","1.2",None,None,1,"container","control",None),
("AdwPreferencesDialog","AdwDialog","libadwaita","1.5",None,None,1,"container","toplevel",None),
("AdwPreferencesGroup","GtkWidget","libadwaita",None,None,None,1,"container","layout",None),
@ -88,6 +93,8 @@
("AdwToast","GObject","libadwaita",None,None,None,1,None,"layout",None),
("AdwToastOverlay","GtkWidget","libadwaita",None,None,None,1,"container","layout",None),
("AdwToastPriority","enum","libadwaita",None,None,None,None,None,None,None),
("AdwToggle","GObject","libadwaita","1.7",None,None,1,None,None,None),
("AdwToggleGroup","GtkWidget","libadwaita","1.7",None,None,1,"container",None,None),
("AdwToolbarStyle","enum","libadwaita",None,None,None,None,None,None,None),
("AdwToolbarView","GtkWidget","libadwaita","1.4",None,None,1,"container","layout",None),
("AdwViewStack","GtkWidget","libadwaita",None,None,None,1,"container","layout",None),
@ -98,12 +105,16 @@
("AdwViewSwitcherPolicy","enum","libadwaita",None,None,None,None,None,None,None),
("AdwViewSwitcherTitle","GtkWidget","libadwaita",None,"1.4",None,1,"container","display",None),
("AdwWindow","GtkWindow","libadwaita",None,None,None,1,"container","toplevel",None),
("AdwWindowTitle","GtkWidget","libadwaita",None,None,None,1,"container","display",None)
("AdwWindowTitle","GtkWidget","libadwaita",None,None,None,1,"container","display",None),
("AdwWrapBox","GtkWidget","libadwaita","1.7",None,None,1,"container",None,None),
("AdwWrapLayout","GtkLayoutManager","libadwaita","1.7",None,None,1,"manager",None,None),
("AdwWrapPolicy","enum","libadwaita",None,None,None,None,None,None,None)
</type>
<type_iface>
("AdwAboutDialog","GtkAccessible"),
("AdwAboutDialog","GtkBuildable"),
("AdwAboutDialog","GtkConstraintTarget"),
("AdwAboutDialog","GtkShortcutManager"),
("AdwAboutWindow","GtkAccessible"),
("AdwAboutWindow","GtkBuildable"),
("AdwAboutWindow","GtkConstraintTarget"),
@ -117,6 +128,7 @@
("AdwAlertDialog","GtkAccessible"),
("AdwAlertDialog","GtkBuildable"),
("AdwAlertDialog","GtkConstraintTarget"),
("AdwAlertDialog","GtkShortcutManager"),
("AdwApplicationWindow","GtkAccessible"),
("AdwApplicationWindow","GtkBuildable"),
("AdwApplicationWindow","GtkConstraintTarget"),
@ -178,6 +190,7 @@
("AdwDialog","GtkAccessible"),
("AdwDialog","GtkBuildable"),
("AdwDialog","GtkConstraintTarget"),
("AdwDialog","GtkShortcutManager"),
("AdwEntryRow","GtkAccessible"),
("AdwEntryRow","GtkActionable"),
("AdwEntryRow","GtkBuildable"),
@ -196,6 +209,10 @@
("AdwHeaderBar","GtkAccessible"),
("AdwHeaderBar","GtkBuildable"),
("AdwHeaderBar","GtkConstraintTarget"),
("AdwInlineViewSwitcher","GtkAccessible"),
("AdwInlineViewSwitcher","GtkBuildable"),
("AdwInlineViewSwitcher","GtkConstraintTarget"),
("AdwInlineViewSwitcher","GtkOrientable"),
("AdwLayout","GtkBuildable"),
("AdwLayoutSlot","GtkAccessible"),
("AdwLayoutSlot","GtkBuildable"),
@ -236,6 +253,7 @@
("AdwPreferencesDialog","GtkAccessible"),
("AdwPreferencesDialog","GtkBuildable"),
("AdwPreferencesDialog","GtkConstraintTarget"),
("AdwPreferencesDialog","GtkShortcutManager"),
("AdwPreferencesGroup","GtkAccessible"),
("AdwPreferencesGroup","GtkBuildable"),
("AdwPreferencesGroup","GtkConstraintTarget"),
@ -294,6 +312,10 @@
("AdwToastOverlay","GtkAccessible"),
("AdwToastOverlay","GtkBuildable"),
("AdwToastOverlay","GtkConstraintTarget"),
("AdwToggleGroup","GtkAccessible"),
("AdwToggleGroup","GtkBuildable"),
("AdwToggleGroup","GtkConstraintTarget"),
("AdwToggleGroup","GtkOrientable"),
("AdwToolbarView","GtkAccessible"),
("AdwToolbarView","GtkBuildable"),
("AdwToolbarView","GtkConstraintTarget"),
@ -320,7 +342,12 @@
("AdwWindow","GtkShortcutManager"),
("AdwWindowTitle","GtkAccessible"),
("AdwWindowTitle","GtkBuildable"),
("AdwWindowTitle","GtkConstraintTarget")
("AdwWindowTitle","GtkConstraintTarget"),
("AdwWrapBox","GtkAccessible"),
("AdwWrapBox","GtkBuildable"),
("AdwWrapBox","GtkConstraintTarget"),
("AdwWrapBox","GtkOrientable"),
("AdwWrapLayout","GtkOrientable")
</type_iface>
<type_enum>
("AdwAccentColor","ADW_ACCENT_COLOR_BLUE","blue",None,"Use a blue color (`#3584e4`). This is the default value."),
@ -336,6 +363,8 @@
("AdwAnimationState","ADW_ANIMATION_IDLE","idle",None,"The animation hasn't started yet."),
("AdwAnimationState","ADW_ANIMATION_PAUSED","paused",1,"The animation has been paused."),
("AdwAnimationState","ADW_ANIMATION_PLAYING","playing",2,"The animation is currently playing."),
("AdwBannerButtonStyle","ADW_BANNER_BUTTON_DEFAULT","default",None,"The default button style."),
("AdwBannerButtonStyle","ADW_BANNER_BUTTON_SUGGESTED","suggested",1,"A button in the suggested action style."),
("AdwBreakpointConditionLengthType","ADW_BREAKPOINT_CONDITION_MAX_HEIGHT","max-height",3,"true if the height is less than or equal to the condition value"),
("AdwBreakpointConditionLengthType","ADW_BREAKPOINT_CONDITION_MAX_WIDTH","max-width",1,"true if the width is less than or equal to the condition value"),
("AdwBreakpointConditionLengthType","ADW_BREAKPOINT_CONDITION_MIN_HEIGHT","min-height",2,"true if the height is greater than or equal to the condition value"),
@ -352,12 +381,15 @@
("AdwDialogPresentationMode","ADW_DIALOG_AUTO","auto",None,"Switch between `ADW_DIALOG_FLOATING` and `ADW_DIALOG_BOTTOM_SHEET` depending on available size."),
("AdwDialogPresentationMode","ADW_DIALOG_BOTTOM_SHEET","bottom-sheet",2,"Present dialog as a bottom sheet."),
("AdwDialogPresentationMode","ADW_DIALOG_FLOATING","floating",1,"Present dialog as a centered floating window."),
("AdwEasing","ADW_EASE","ease",31,"Cubic bezier tweening, with control points in (0.25, 0.1) and (0.25, 1.0). Increases in velocity towards the middle of the animation, slowing back down at the end."),
("AdwEasing","ADW_EASE_IN","ease-in",32,"Cubic bezier tweening, with control points in (0.42, 0.0) and (1.0, 1.0). Starts off slowly, with the speed of the animation increasing until complete."),
("AdwEasing","ADW_EASE_IN_BACK","ease-in-back",25,"Overshooting cubic tweening, with backtracking on start."),
("AdwEasing","ADW_EASE_IN_BOUNCE","ease-in-bounce",28,"Exponentially decaying parabolic (bounce) tweening, on start."),
("AdwEasing","ADW_EASE_IN_CIRC","ease-in-circ",19,"Circular tweening."),
("AdwEasing","ADW_EASE_IN_CUBIC","ease-in-cubic",4,"Cubic tweening."),
("AdwEasing","ADW_EASE_IN_ELASTIC","ease-in-elastic",22,"Elastic tweening, with offshoot on start."),
("AdwEasing","ADW_EASE_IN_EXPO","ease-in-expo",16,"Exponential tweening."),
("AdwEasing","ADW_EASE_IN_OUT","ease-in-out",34,"Cubic bezier tweening, with control points in (0.42, 0.0) and (0.58, 1.0). Starts off slowly, speeds up in the middle, and then slows down again."),
("AdwEasing","ADW_EASE_IN_OUT_BACK","ease-in-out-back",27,"Overshooting cubic tweening, with backtracking on both ends, combining `ADW_EASE_IN_BACK` and `ADW_EASE_OUT_BACK`."),
("AdwEasing","ADW_EASE_IN_OUT_BOUNCE","ease-in-out-bounce",30,"Exponentially decaying parabolic (bounce) tweening, with bounce on both ends, combining `ADW_EASE_IN_BOUNCE` and `ADW_EASE_OUT_BOUNCE`."),
("AdwEasing","ADW_EASE_IN_OUT_CIRC","ease-in-out-circ",21,"Circular tweening, combining `ADW_EASE_IN_CIRC` and `ADW_EASE_OUT_CIRC`."),
@ -372,6 +404,7 @@
("AdwEasing","ADW_EASE_IN_QUART","ease-in-quart",7,"Quartic tweening."),
("AdwEasing","ADW_EASE_IN_QUINT","ease-in-quint",10,"Quintic tweening."),
("AdwEasing","ADW_EASE_IN_SINE","ease-in-sine",13,"Sine wave tweening."),
("AdwEasing","ADW_EASE_OUT","ease-out",33,"Cubic bezier tweening, with control points in (0.0, 0.0) and (0.58, 1.0). Starts quickly, slowing down the animation until complete."),
("AdwEasing","ADW_EASE_OUT_BACK","ease-out-back",26,"Overshooting cubic tweening, with backtracking on end, inverse of `ADW_EASE_IN_BACK`."),
("AdwEasing","ADW_EASE_OUT_BOUNCE","ease-out-bounce",29,"Exponentially decaying parabolic (bounce) tweening, with bounce on end, inverse of `ADW_EASE_IN_BOUNCE`."),
("AdwEasing","ADW_EASE_OUT_CIRC","ease-out-circ",20,"Circular tweening, inverse of `ADW_EASE_IN_CIRC`."),
@ -391,6 +424,12 @@
("AdwFlapTransitionType","ADW_FLAP_TRANSITION_TYPE_UNDER","under",1,"The content slides over the flap. Only the content can be swiped."),
("AdwFoldThresholdPolicy","ADW_FOLD_THRESHOLD_POLICY_MINIMUM","minimum",None,"Folding is based on the minimum size"),
("AdwFoldThresholdPolicy","ADW_FOLD_THRESHOLD_POLICY_NATURAL","natural",1,"Folding is based on the natural size"),
("AdwInlineViewSwitcherDisplayMode","ADW_INLINE_VIEW_SWITCHER_BOTH","both",2,"Toggles display both icons and labels."),
("AdwInlineViewSwitcherDisplayMode","ADW_INLINE_VIEW_SWITCHER_ICONS","icons",1,"Toggles only display icons."),
("AdwInlineViewSwitcherDisplayMode","ADW_INLINE_VIEW_SWITCHER_LABELS","labels",None,"Toggles only display labels."),
("AdwJustifyMode","ADW_JUSTIFY_FILL","fill",1,"Stretch each child within the line, keeping consistent spacing, so that the line fills the entire length."),
("AdwJustifyMode","ADW_JUSTIFY_NONE","none",None,"Don't justify children within a line."),
("AdwJustifyMode","ADW_JUSTIFY_SPREAD","spread",2,"Increase spacing between children, moving the children so that the first and last child are aligned with the beginning and end of the line. If the line only contains a single widget, it will be stretched regardless."),
("AdwLeafletTransitionType","ADW_LEAFLET_TRANSITION_TYPE_OVER","over",None,"Cover the old page or uncover the new page, sliding from or towards the end according to orientation, text direction and children order"),
("AdwLeafletTransitionType","ADW_LEAFLET_TRANSITION_TYPE_SLIDE","slide",2,"Slide from left, right, up or down according to the orientation, text direction and the children order"),
("AdwLeafletTransitionType","ADW_LEAFLET_TRANSITION_TYPE_UNDER","under",1,"Uncover the new page or cover the old page, sliding from or towards the start according to orientation, text direction and children order"),
@ -399,6 +438,8 @@
("AdwLengthUnit","ADW_LENGTH_UNIT_SP","sp",2,"scale independent pixels, changes with text scale factor"),
("AdwNavigationDirection","ADW_NAVIGATION_DIRECTION_BACK","back",None,"Corresponds to start or top, depending on orientation and text direction"),
("AdwNavigationDirection","ADW_NAVIGATION_DIRECTION_FORWARD","forward",1,"Corresponds to end or bottom, depending on orientation and text direction"),
("AdwPackDirection","ADW_PACK_END_TO_START","end-to-start",1,"Pack children from right to left for LTR languages, or bottom to top vertically."),
("AdwPackDirection","ADW_PACK_START_TO_END","start-to-end",None,"Pack children from left to right for LTR languages, or top to bottom vertically."),
("AdwResponseAppearance","ADW_RESPONSE_DEFAULT","default",None,"the default appearance."),
("AdwResponseAppearance","ADW_RESPONSE_DESTRUCTIVE","destructive",2,"used to draw attention to the potentially damaging consequences of using the response. This appearance acts as a warning to the user."),
("AdwResponseAppearance","ADW_RESPONSE_SUGGESTED","suggested",1,"used to denote important responses such as the affirmative action."),
@ -410,7 +451,9 @@
("AdwToolbarStyle","ADW_TOOLBAR_RAISED","raised",1,"Opaque background with a persistent shadow"),
("AdwToolbarStyle","ADW_TOOLBAR_RAISED_BORDER","raised-border",2,"Opaque background with a persistent border"),
("AdwViewSwitcherPolicy","ADW_VIEW_SWITCHER_POLICY_NARROW","narrow",None,"Force the narrow mode"),
("AdwViewSwitcherPolicy","ADW_VIEW_SWITCHER_POLICY_WIDE","wide",1,"Force the wide mode")
("AdwViewSwitcherPolicy","ADW_VIEW_SWITCHER_POLICY_WIDE","wide",1,"Force the wide mode"),
("AdwWrapPolicy","ADW_WRAP_MINIMUM","minimum",None,"Fit as many children into each line as possible, shrinking them down to their minimum size before wrapping to the next line."),
("AdwWrapPolicy","ADW_WRAP_NATURAL","natural",1,"Wrap to the next line as soon as the previous line cannot fit any more children without shrinking them past their natural size.")
</type_enum>
<type_flags>
("AdwTabViewShortcuts","ADW_TAB_VIEW_SHORTCUT_ALL_SHORTCUTS","all-shortcuts",4095,"All of the shortcuts"),
@ -513,6 +556,8 @@
("AdwAnimation","follow-enable-animations-setting","gboolean",None,None,None,"True",None,None,"1.3",None,None,None,None,None,None,None,None),
("AdwAnimation","target","AdwAnimationTarget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("AdwAnimation","widget","GtkWidget",1,1,None,None,None,None,None,None,None,None,None,None,None,None,None),
("AdwApplicationWindow","adaptive-preview","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwApplicationWindow","child","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,"GtkWindow",1),
("AdwApplicationWindow","content","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("AdwApplicationWindow","titlebar","GtkWidget",1,None,None,None,None,None,"4.6",None,None,None,None,None,None,"GtkWindow",1),
("AdwAvatar","icon-name","CmbIconName",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
@ -520,6 +565,7 @@
("AdwAvatar","size","gint",None,None,None,"-1","-1","2147483647",None,None,None,None,None,None,None,None,None),
("AdwAvatar","text","gchararray",None,None,None,"",None,None,None,None,None,None,None,None,None,None,None),
("AdwBanner","button-label","gchararray",None,None,None,"",None,None,"1.3",None,None,None,None,None,None,None,None),
("AdwBanner","button-style","AdwBannerButtonStyle",None,None,None,"default",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwBanner","revealed","gboolean",None,None,None,"False",None,None,"1.3",None,None,None,None,None,None,None,None),
("AdwBanner","title","gchararray",None,None,None,"",None,None,"1.3",None,1,None,None,None,None,None,None),
("AdwBanner","use-markup","gboolean",None,None,None,"True",None,None,"1.3",None,None,None,None,None,None,None,None),
@ -532,6 +578,7 @@
("AdwBottomSheet","full-width","gboolean",None,None,None,"True",None,None,"1.6",None,None,None,None,None,None,None,None),
("AdwBottomSheet","modal","gboolean",None,None,None,"True",None,None,"1.6",None,None,None,None,None,None,None,None),
("AdwBottomSheet","open","gboolean",None,None,None,"False",None,None,"1.6",None,None,None,None,None,None,None,None),
("AdwBottomSheet","reveal-bottom-bar","gboolean",None,None,None,"True",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwBottomSheet","sheet","GtkWidget",1,None,None,None,None,None,"1.6",None,None,None,None,None,None,None,None),
("AdwBottomSheet","show-drag-handle","gboolean",None,None,None,"True",None,None,"1.6",None,None,None,None,None,None,None,None),
("AdwBreakpointBin","child","GtkWidget",1,None,None,None,None,None,"1.4",None,None,None,None,None,None,None,None),
@ -615,6 +662,10 @@
("AdwHeaderBar","show-start-title-buttons","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("AdwHeaderBar","show-title","gboolean",None,None,None,"True",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwHeaderBar","title-widget","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("AdwInlineViewSwitcher","can-shrink","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwInlineViewSwitcher","display-mode","AdwInlineViewSwitcherDisplayMode",None,None,None,"labels",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwInlineViewSwitcher","homogeneous","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwInlineViewSwitcher","stack","AdwViewStack",1,None,None,None,None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwLayout","content","GtkWidget",1,1,None,None,None,None,"1.6",None,None,None,None,None,None,None,None),
("AdwLayout","name","gchararray",None,None,None,None,None,None,"1.6",None,None,None,None,None,None,None,None),
("AdwLayoutSlot","id","gchararray",None,1,None,None,None,None,"1.6",None,None,None,None,None,None,None,None),
@ -649,10 +700,13 @@
("AdwNavigationSplitView","min-sidebar-width","gdouble",None,None,None,"180.0","0.0","1.79769313486232e+308","1.4",None,None,None,None,None,None,None,None),
("AdwNavigationSplitView","show-content","gboolean",None,None,None,"False",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwNavigationSplitView","sidebar","AdwNavigationPage",1,None,None,None,None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwNavigationSplitView","sidebar-position","GtkPackType",None,None,None,"start",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwNavigationSplitView","sidebar-width-fraction","gdouble",None,None,None,"0.25","0.0","1.0","1.4",None,None,None,None,None,None,None,None),
("AdwNavigationSplitView","sidebar-width-unit","AdwLengthUnit",None,None,None,"sp",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwNavigationView","animate-transitions","gboolean",None,None,None,"True",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwNavigationView","hhomogeneous","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwNavigationView","pop-on-escape","gboolean",None,None,None,"True",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwNavigationView","vhomogeneous","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwOverlaySplitView","collapsed","gboolean",None,None,None,"False",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwOverlaySplitView","content","GtkWidget",1,None,None,None,None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwOverlaySplitView","enable-hide-gesture","gboolean",None,None,None,"True",None,None,"1.4",None,None,None,None,None,None,None,None),
@ -676,6 +730,7 @@
("AdwPreferencesGroup","header-suffix","GtkWidget",1,None,None,None,None,None,"1.1",None,None,None,None,None,None,None,None),
("AdwPreferencesGroup","separate-rows","gboolean",None,None,None,"False",None,None,"1.6",None,None,None,None,None,None,None,None),
("AdwPreferencesGroup","title","gchararray",None,None,None,"",None,None,None,None,1,None,None,None,None,None,None),
("AdwPreferencesPage","banner","AdwBanner",1,None,None,None,None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwPreferencesPage","description","gchararray",None,None,None,"",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwPreferencesPage","description-centered","gboolean",None,None,None,"False",None,None,"1.6",None,None,None,None,None,None,None,None),
("AdwPreferencesPage","icon-name","CmbIconName",None,None,None,"",None,None,None,None,None,None,None,None,None,None,None),
@ -795,6 +850,17 @@
("AdwToast","title","gchararray",None,None,None,"",None,None,None,None,1,None,None,None,None,None,None),
("AdwToast","use-markup","gboolean",None,None,None,"True",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwToastOverlay","child","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("AdwToggle","child","GtkWidget",1,None,None,None,None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToggle","enabled","gboolean",None,None,None,"True",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToggle","icon-name","gchararray",None,None,None,None,None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToggle","label","gchararray",None,None,None,None,None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToggle","name","gchararray",None,None,None,None,None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToggle","tooltip","gchararray",None,None,None,"",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToggle","use-underline","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToggleGroup","active","guint",None,None,None,"4294967295","0","4294967295","1.7",None,None,None,None,None,None,None,None),
("AdwToggleGroup","active-name","gchararray",None,None,None,None,None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToggleGroup","can-shrink","gboolean",None,None,None,"True",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToggleGroup","homogeneous","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwToolbarView","bottom-bar-style","AdwToolbarStyle",None,None,None,"flat",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwToolbarView","content","GtkWidget",1,None,None,None,None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwToolbarView","extend-content-to-bottom-edge","gboolean",None,None,None,"False",None,None,"1.4",None,None,None,None,None,None,None,None),
@ -802,7 +868,9 @@
("AdwToolbarView","reveal-bottom-bars","gboolean",None,None,None,"True",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwToolbarView","reveal-top-bars","gboolean",None,None,None,"True",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwToolbarView","top-bar-style","AdwToolbarStyle",None,None,None,"flat",None,None,"1.4",None,None,None,None,None,None,None,None),
("AdwViewStack","enable-transitions","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwViewStack","hhomogeneous","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("AdwViewStack","transition-duration","guint",None,None,None,"200","0","4294967295","1.7",None,None,None,None,None,None,None,None),
("AdwViewStack","vhomogeneous","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("AdwViewStack","visible-child","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("AdwViewStack","visible-child-name","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
@ -823,9 +891,38 @@
("AdwViewSwitcherTitle","subtitle","gchararray",None,None,None,"",None,None,None,"1.4",1,None,None,None,None,None,None),
("AdwViewSwitcherTitle","title","gchararray",None,None,None,"",None,None,None,"1.4",1,None,None,None,None,None,None),
("AdwViewSwitcherTitle","view-switcher-enabled","gboolean",None,None,None,"True",None,None,None,"1.4",None,None,None,None,None,None,None),
("AdwWindow","adaptive-preview","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWindow","child","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,"GtkWindow",1),
("AdwWindow","content","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("AdwWindow","titlebar","GtkWidget",1,None,None,None,None,None,"4.6",None,None,None,None,None,None,"GtkWindow",1),
("AdwWindowTitle","subtitle","gchararray",None,None,None,"",None,None,None,None,1,None,None,None,None,None,None),
("AdwWindowTitle","title","gchararray",None,None,None,"",None,None,None,None,1,None,None,None,None,None,None)
("AdwWindowTitle","title","gchararray",None,None,None,"",None,None,None,None,1,None,None,None,None,None,None),
("AdwWrapBox","align","gfloat",None,None,None,"0.0","0.0","1.0","1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","child-spacing","gint",None,None,None,"0","0","2147483647","1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","child-spacing-unit","AdwLengthUnit",None,None,None,"px",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","justify","AdwJustifyMode",None,None,None,"none",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","justify-last-line","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","line-homogeneous","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","line-spacing","gint",None,None,None,"0","0","2147483647","1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","line-spacing-unit","AdwLengthUnit",None,None,None,"px",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","natural-line-length","gint",None,None,None,"-1","-1","2147483647","1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","natural-line-length-unit","AdwLengthUnit",None,None,None,"px",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","pack-direction","AdwPackDirection",None,None,None,"start-to-end",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","wrap-policy","AdwWrapPolicy",None,None,None,"natural",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapBox","wrap-reverse","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","align","gfloat",None,None,None,"0.0","0.0","1.0","1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","child-spacing","gint",None,None,None,"0","0","2147483647","1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","child-spacing-unit","AdwLengthUnit",None,None,None,"px",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","justify","AdwJustifyMode",None,None,None,"none",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","justify-last-line","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","line-homogeneous","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","line-spacing","gint",None,None,None,"0","0","2147483647","1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","line-spacing-unit","AdwLengthUnit",None,None,None,"px",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","natural-line-length","gint",None,None,None,"-1","-1","2147483647","1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","natural-line-length-unit","AdwLengthUnit",None,None,None,"px",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","pack-direction","AdwPackDirection",None,None,None,"start-to-end",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","wrap-policy","AdwWrapPolicy",None,None,None,"natural",None,None,"1.7",None,None,None,None,None,None,None,None),
("AdwWrapLayout","wrap-reverse","gboolean",None,None,None,"False",None,None,"1.7",None,None,None,None,None,None,None,None)
</property>
<signal>
("AdwAboutDialog","activate-link","1.5",None,None),

View File

@ -204,7 +204,7 @@
("HdyHeaderBar","title","gchararray",None,None,None,None,None,None,"1.0",None,1,None,None,None,None,None,None),
("HdyHeaderBar","transition-duration","guint",None,None,None,"200","0","4294967295","1.0",None,None,None,None,None,None,None,None),
("HdyHeaderBarLayoutChild","pack-type","GtkPackType",None,None,None,"start",None,None,None,None,None,None,None,None,None,None,None),
("HdyHeaderBarLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,1,None,None,None,None),
("HdyHeaderBarLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,None,None,None,None,None),
("HdyHeaderGroup","decorate-all","gboolean",None,None,None,"False",None,None,"1.0",None,None,None,None,None,None,None,None),
("HdyKeypad","column-spacing","guint",None,None,None,"6","0","32767","1.0",None,None,None,None,None,None,None,None),
("HdyKeypad","end-action","GtkWidget",1,None,None,None,None,None,"1.0",None,None,None,None,None,None,None,None),

View File

@ -46,6 +46,8 @@
("WebKitUserScriptInjectionTime","enum","webkit2gtk",None,None,None,None,None,None,None),
("WebKitUserStyleLevel","enum","webkit2gtk",None,None,None,None,None,None,None),
("WebKitWebContext","GObject","webkit2gtk",None,None,None,1,None,None,None),
("WebKitWebExtensionMatchPatternError","enum","webkit2gtk",None,None,None,None,None,None,None),
("WebKitWebExtensionMatchPatternOptions","flags","webkit2gtk",None,None,None,None,None,None,None),
("WebKitWebExtensionMode","enum","webkit2gtk",None,None,None,None,None,None,None),
("WebKitWebProcessTerminationReason","enum","webkit2gtk",None,None,None,None,None,None,None),
("WebKitWebView","WebKitWebViewBase","webkit2gtk",None,None,None,1,"container",None,"MrgDummyWebViewProxy"),
@ -223,6 +225,10 @@
("WebKitUserScriptInjectionTime","WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START","start",None,"Insert the code of the user script at the beginning of loaded documents. This is the default."),
("WebKitUserStyleLevel","WEBKIT_USER_STYLE_LEVEL_AUTHOR","author",1,"The style sheet will be treated as if it was provided by the loaded documents. That means other user style sheets may still override it."),
("WebKitUserStyleLevel","WEBKIT_USER_STYLE_LEVEL_USER","user",None,"The style sheet is an user style sheet, its contents always override other style sheets. This is the default."),
("WebKitWebExtensionMatchPatternError","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_ERROR_INVALID_HOST","invalid-host",809,"The host component was invalid."),
("WebKitWebExtensionMatchPatternError","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_ERROR_INVALID_PATH","invalid-path",810,"The path component was invalid."),
("WebKitWebExtensionMatchPatternError","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_ERROR_INVALID_SCHEME","invalid-scheme",808,"The scheme component was invalid."),
("WebKitWebExtensionMatchPatternError","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_ERROR_UNKNOWN","unknown",899,"An unknown error occured."),
("WebKitWebExtensionMode","WEBKIT_WEB_EXTENSION_MODE_MANIFESTV2","manifestv2",1,"For a ManifestV2 extension."),
("WebKitWebExtensionMode","WEBKIT_WEB_EXTENSION_MODE_MANIFESTV3","manifestv3",2,"For a ManifestV3 extension."),
("WebKitWebExtensionMode","WEBKIT_WEB_EXTENSION_MODE_NONE","none",None,"Not for an extension."),
@ -259,6 +265,10 @@
("WebKitSnapshotOptions","WEBKIT_SNAPSHOT_OPTIONS_INCLUDE_SELECTION_HIGHLIGHTING","include-selection-highlighting",1,"Whether to include in the snapshot the highlight of the selected content."),
("WebKitSnapshotOptions","WEBKIT_SNAPSHOT_OPTIONS_NONE","none",None,"Do not include any special options."),
("WebKitSnapshotOptions","WEBKIT_SNAPSHOT_OPTIONS_TRANSPARENT_BACKGROUND","transparent-background",2,"Do not fill the background with white before rendering the snapshot. Since 2.8"),
("WebKitWebExtensionMatchPatternOptions","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_OPTIONS_IGNORE_PATHS","ignore-paths",4,"The host components should be ignored while matching."),
("WebKitWebExtensionMatchPatternOptions","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_OPTIONS_IGNORE_SCHEMES","ignore-schemes",2,"The scheme components should be ignored while matching."),
("WebKitWebExtensionMatchPatternOptions","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_OPTIONS_MATCH_BIDIRECTIONALLY","match-bidirectionally",8,"Two patterns should be checked in either direction while matching (A matches B, or B matches A). Invalid for matching URLs."),
("WebKitWebExtensionMatchPatternOptions","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_OPTIONS_NONE","none",1,"No special matching options."),
("WebKitWebsiteDataTypes","WEBKIT_WEBSITE_DATA_ALL","all",16383,"All types."),
("WebKitWebsiteDataTypes","WEBKIT_WEBSITE_DATA_COOKIES","cookies",256,"Cookies."),
("WebKitWebsiteDataTypes","WEBKIT_WEBSITE_DATA_DEVICE_ID_HASH_SALT","device-id-hash-salt",512,"Hash salt used to generate the device ids used by webpages. Since 2.24"),
@ -337,6 +347,7 @@
("WebKitSettings","sans-serif-font-family","gchararray",None,None,None,"sans-serif",None,None,None,None,None,None,None,None,None,None,None),
("WebKitSettings","serif-font-family","gchararray",None,None,None,"serif",None,None,None,None,None,None,None,None,None,None,None),
("WebKitSettings","user-agent","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("WebKitSettings","webrtc-udp-ports-range","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("WebKitSettings","zoom-text-only","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("WebKitWebContext","local-storage-directory","gchararray",None,1,None,None,None,None,None,None,None,None,None,None,None,None,None),
("WebKitWebContext","process-swap-on-cross-site-navigation-enabled","gboolean",None,1,None,"False",None,None,None,None,None,None,None,None,None,None,None),

View File

@ -45,6 +45,8 @@
("WebKitUserScriptInjectionTime","enum","webkitgtk",None,None,None,None,None,None,None),
("WebKitUserStyleLevel","enum","webkitgtk",None,None,None,None,None,None,None),
("WebKitWebContext","GObject","webkitgtk",None,None,None,1,None,None,None),
("WebKitWebExtensionMatchPatternError","enum","webkitgtk",None,None,None,None,None,None,None),
("WebKitWebExtensionMatchPatternOptions","flags","webkitgtk",None,None,None,None,None,None,None),
("WebKitWebExtensionMode","enum","webkitgtk",None,None,None,None,None,None,None),
("WebKitWebProcessTerminationReason","enum","webkitgtk",None,None,None,None,None,None,None),
("WebKitWebView","WebKitWebViewBase","webkitgtk",None,None,None,1,"container",None,"MrgDummyWebViewProxy"),
@ -218,6 +220,10 @@
("WebKitUserScriptInjectionTime","WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START","start",None,"Insert the code of the user script at the beginning of loaded documents. This is the default."),
("WebKitUserStyleLevel","WEBKIT_USER_STYLE_LEVEL_AUTHOR","author",1,"The style sheet will be treated as if it was provided by the loaded documents. That means other user style sheets may still override it."),
("WebKitUserStyleLevel","WEBKIT_USER_STYLE_LEVEL_USER","user",None,"The style sheet is an user style sheet, its contents always override other style sheets. This is the default."),
("WebKitWebExtensionMatchPatternError","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_ERROR_INVALID_HOST","invalid-host",809,"The host component was invalid."),
("WebKitWebExtensionMatchPatternError","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_ERROR_INVALID_PATH","invalid-path",810,"The path component was invalid."),
("WebKitWebExtensionMatchPatternError","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_ERROR_INVALID_SCHEME","invalid-scheme",808,"The scheme component was invalid."),
("WebKitWebExtensionMatchPatternError","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_ERROR_UNKNOWN","unknown",899,"An unknown error occured."),
("WebKitWebExtensionMode","WEBKIT_WEB_EXTENSION_MODE_MANIFESTV2","manifestv2",1,"For a ManifestV2 extension."),
("WebKitWebExtensionMode","WEBKIT_WEB_EXTENSION_MODE_MANIFESTV3","manifestv3",2,"For a ManifestV3 extension."),
("WebKitWebExtensionMode","WEBKIT_WEB_EXTENSION_MODE_NONE","none",None,"Not for an extension."),
@ -254,6 +260,10 @@
("WebKitSnapshotOptions","WEBKIT_SNAPSHOT_OPTIONS_INCLUDE_SELECTION_HIGHLIGHTING","include-selection-highlighting",1,"Whether to include in the snapshot the highlight of the selected content."),
("WebKitSnapshotOptions","WEBKIT_SNAPSHOT_OPTIONS_NONE","none",None,"Do not include any special options."),
("WebKitSnapshotOptions","WEBKIT_SNAPSHOT_OPTIONS_TRANSPARENT_BACKGROUND","transparent-background",2,"Do not fill the background with white before rendering the snapshot. Since 2.8"),
("WebKitWebExtensionMatchPatternOptions","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_OPTIONS_IGNORE_PATHS","ignore-paths",4,"The host components should be ignored while matching."),
("WebKitWebExtensionMatchPatternOptions","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_OPTIONS_IGNORE_SCHEMES","ignore-schemes",2,"The scheme components should be ignored while matching."),
("WebKitWebExtensionMatchPatternOptions","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_OPTIONS_MATCH_BIDIRECTIONALLY","match-bidirectionally",8,"Two patterns should be checked in either direction while matching (A matches B, or B matches A). Invalid for matching URLs."),
("WebKitWebExtensionMatchPatternOptions","WEBKIT_WEB_EXTENSION_MATCH_PATTERN_OPTIONS_NONE","none",1,"No special matching options."),
("WebKitWebsiteDataTypes","WEBKIT_WEBSITE_DATA_ALL","all",4095,"All types."),
("WebKitWebsiteDataTypes","WEBKIT_WEBSITE_DATA_COOKIES","cookies",64,"Cookies."),
("WebKitWebsiteDataTypes","WEBKIT_WEBSITE_DATA_DEVICE_ID_HASH_SALT","device-id-hash-salt",128,"Hash salt used to generate the device ids used by webpages."),
@ -324,6 +334,7 @@
("WebKitSettings","sans-serif-font-family","gchararray",None,None,None,"sans-serif",None,None,None,None,None,None,None,None,None,None,None),
("WebKitSettings","serif-font-family","gchararray",None,None,None,"serif",None,None,None,None,None,None,None,None,None,None,None),
("WebKitSettings","user-agent","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("WebKitSettings","webrtc-udp-ports-range","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("WebKitSettings","zoom-text-only","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("WebKitWebContext","time-zone-override","gchararray",None,1,None,None,None,None,None,None,None,None,None,None,None,None,None),
("WebKitWebView","automation-presentation-type","WebKitAutomationBrowsingContextPresentation",None,1,None,"window",None,None,None,None,None,None,None,None,None,None,None),

View File

@ -24,6 +24,12 @@
</properties>
</GtkAccessible>
<GtkAssistant>
<internal-children>
<child name="action_area"/>
</internal-children>
</GtkAssistant>
<GtkAssistantPage>
<properties>
<property id="title" translatable="True"/>
@ -80,54 +86,6 @@
</properties>
</GtkPrintJob>
<GtkBoxLayoutChild>
<properties>
<property id="position" target="Gtk+-3.0" is-position="True"/>
</properties>
</GtkBoxLayoutChild>
<GtkHeaderBarLayoutChild>
<properties>
<property id="position" target="Gtk+-3.0" is-position="True"/>
</properties>
</GtkHeaderBarLayoutChild>
<GtkNotebookLayoutChild>
<properties>
<property id="position" target="Gtk+-3.0" is-position="True"/>
</properties>
</GtkNotebookLayoutChild>
<GtkActionBarLayoutChild>
<properties>
<property id="position" target="Gtk+-3.0" is-position="True"/>
</properties>
</GtkActionBarLayoutChild>
<GtkPopoverMenuBarLayoutChild>
<properties>
<property id="position" target="Gtk+-3.0" is-position="True"/>
</properties>
</GtkPopoverMenuBarLayoutChild>
<GtkStackLayoutChild>
<properties>
<property id="position" target="Gtk+-3.0" is-position="True"/>
</properties>
</GtkStackLayoutChild>
<GtkToolItemGroupLayoutChild>
<properties>
<property id="position" target="Gtk+-3.0" is-position="True"/>
</properties>
</GtkToolItemGroupLayoutChild>
<GtkPopoverMenuLayoutChild>
<properties>
<property id="position" target="Gtk+-3.0" is-position="True"/>
</properties>
</GtkPopoverMenuLayoutChild>
<GtkGridLayoutChild>
<properties target="Gtk+-3.0">
<property id="left-attach" save-always="True"/>
@ -145,6 +103,17 @@
<action-widget response="gint">GtkWidget</action-widget>
</action-widgets>
</data>
<children-types>
<type>action</type>
</children-types>
<internal-children target="Gtk-4.0">
<child name="action_area"/>
</internal-children>
<internal-children target="Gtk+-3.0">
<child name="vbox">
<child name="action_area"/>
</child>
</internal-children>
</GtkDialog>
<GtkListStore>
@ -246,7 +215,7 @@
<GtkComboBoxText>
<data>
<items>
<item id="gchararray">gchararray</item>
<item id="gchararray" Cmb:translatable="True">gchararray</item>
</items>
</data>
</GtkComboBoxText>
@ -321,6 +290,9 @@
<properties>
<property id="tearoff-title" translatable="True"/>
</properties>
<internal-children>
<child name="entry" type="GtkEntry" creation-property-id="has-entry"/>
</internal-children>
</GtkComboBox>
<GtkAppChooserButton>
@ -384,6 +356,12 @@
</properties>
</GtkAction>
<GtkTreeView>
<internal-children>
<child name="selection" anarchist="True"/>
</internal-children>
</GtkTreeView>
<GtkTreeViewColumn>
<properties>
<property id="title" translatable="True"/>
@ -566,6 +544,45 @@
</data>
</GtkFileFilter>
<GtkScaleButton>
<internal-children>
<child name="plus_button"/>
<child name="minus_button"/>
</internal-children>
</GtkScaleButton>
<GtkInfoBar>
<internal-children target="Gtk+-3.0">
<child name="action_area"/>
<child name="content_area"/>
</internal-children>
</GtkInfoBar>
<GtkColorSelectionDialog target="Gtk+-3.0">
<internal-children>
<child name="vbox">
<child name="color_selection"/>
</child>
<child name="action_area">
<child name="ok_button"/>
<child name="cancel_button"/>
<child name="help_button"/>
</child>
</internal-children>
</GtkColorSelectionDialog>
<GtkFontSelectionDialog target="Gtk+-3.0">
<internal-children>
<child name="vbox">
<child name="font_selection"/>
</child>
<child name="action_area">
<child name="ok_button"/>
<child name="cancel_button"/>
<child name="apply_button"/>
</child>
</internal-children>
</GtkFontSelectionDialog>
</types>
<categories>
@ -577,6 +594,8 @@
<GtkDialog/>
<GtkAboutDialog/>
<GtkFileChooserDialog/>
<GtkColorSelectionDialog/>
<GtkFontSelectionDialog/>
<GtkColorChooserDialog/>
<GtkFontChooserDialog/>
<GtkMessageDialog/>

View File

@ -246,7 +246,7 @@
("GdkFrameClockPhase","GDK_FRAME_CLOCK_PHASE_UPDATE","update",4,"corresponds to GdkFrameClock::update."),
("GdkGLAPI","GDK_GL_API_GL","gl",1,"The OpenGL API"),
("GdkGLAPI","GDK_GL_API_GLES","gles",2,"The OpenGL ES API"),
("GdkModifierType","GDK_ALT_MASK","alt-mask",8,"the fourth modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier, but normally it is the Alt key)."),
("GdkModifierType","GDK_ALT_MASK","alt-mask",8,"the fourth modifier key (it depends on the Windowing System configuration which key is interpreted as this modifier, but normally it is the &lt;kbd&gt;Alt&lt;/kbd&gt; key)."),
("GdkModifierType","GDK_BUTTON1_MASK","button1-mask",256,"the first mouse button."),
("GdkModifierType","GDK_BUTTON2_MASK","button2-mask",512,"the second mouse button."),
("GdkModifierType","GDK_BUTTON3_MASK","button3-mask",1024,"the third mouse button."),
@ -254,7 +254,7 @@
("GdkModifierType","GDK_BUTTON5_MASK","button5-mask",4096,"the fifth mouse button."),
("GdkModifierType","GDK_CONTROL_MASK","control-mask",4,"the Control key."),
("GdkModifierType","GDK_HYPER_MASK","hyper-mask",134217728,"the Hyper modifier."),
("GdkModifierType","GDK_LOCK_MASK","lock-mask",2,"a Lock key (depending on the modifier mapping of the X server this may either be CapsLock or ShiftLock)."),
("GdkModifierType","GDK_LOCK_MASK","lock-mask",2,"a Lock key (depending on the Windowing System configuration, this may either be &lt;kbd&gt;CapsLock&lt;/kbd&gt; or &lt;kbd&gt;ShiftLock&lt;/kbd&gt;)."),
("GdkModifierType","GDK_META_MASK","meta-mask",268435456,"the Meta modifier. Maps to Command on macOS."),
("GdkModifierType","GDK_NO_MODIFIER_MASK","no-modifier-mask",None,"No modifier."),
("GdkModifierType","GDK_SHIFT_MASK","shift-mask",1,"the Shift key."),

View File

@ -79,7 +79,7 @@
("GtkColorChooserDialog","GtkDialog","gtk+","3.4",None,None,1,"container","toplevel",None),
("GtkColorChooserWidget","GtkBox","gtk+","3.4",None,None,1,"container","display",None),
("GtkColorSelection","GtkBox","gtk+",None,None,None,1,"container",None,None),
("GtkColorSelectionDialog","GtkDialog","gtk+",None,None,None,1,"container",None,None),
("GtkColorSelectionDialog","GtkDialog","gtk+",None,None,None,1,"container","toplevel",None),
("GtkComboBox","GtkBin","gtk+",None,None,None,1,"container","control",None),
("GtkComboBoxText","GtkComboBox","gtk+",None,None,None,1,"container","control",None),
("GtkContainer","GtkWidget","gtk+",None,None,1,1,"container",None,None),
@ -130,7 +130,7 @@
("GtkFontChooserLevel","flags","gtk+",None,None,None,None,None,None,None),
("GtkFontChooserWidget","GtkBox","gtk+","3.2",None,None,1,"container","display",None),
("GtkFontSelection","GtkBox","gtk+",None,"3.2",None,1,"container",None,None),
("GtkFontSelectionDialog","GtkDialog","gtk+",None,"3.2",None,1,"container",None,None),
("GtkFontSelectionDialog","GtkDialog","gtk+",None,"3.2",None,1,"container","toplevel",None),
("GtkFrame","GtkBin","gtk+",None,None,None,1,"container","layout",None),
("GtkGLArea","GtkWidget","gtk+","3.16",None,None,1,None,"display",None),
("GtkGesture","GtkEventController","gtk+",None,None,1,1,None,None,None),
@ -1371,7 +1371,7 @@
</type_flags>
<type_data>
("GtkComboBoxText",1,None,"items",None,None),
("GtkComboBoxText",2,1,"item","gchararray",None),
("GtkComboBoxText",2,1,"item","gchararray",1),
("GtkDialog",1,None,"action-widgets",None,None),
("GtkDialog",2,1,"action-widget","GtkWidget",None),
("GtkFileFilter",1,None,"mime-types",None,None),
@ -1414,6 +1414,7 @@
("GtkWidget",2,"name","gchararray")
</type_data_arg>
<type_child_type>
("GtkDialog","action",None,None),
("GtkExpander","label",1,None),
("GtkFrame","label",1,None),
("GtkMenuItem","submenu",None,None),
@ -1425,6 +1426,29 @@
("GtkTextTagTable","tag",None,None),
("GtkWindow","titlebar",1,None)
</type_child_type>
<type_internal_child>
("GtkAssistant","action_area",None,"GtkBox",None),
("GtkColorSelectionDialog","action_area",None,"GtkButtonBox",None),
("GtkColorSelectionDialog","cancel_button","action_area","GtkButton",None),
("GtkColorSelectionDialog","color_selection","vbox","GtkColorSelection",None),
("GtkColorSelectionDialog","help_button","action_area","GtkButton",None),
("GtkColorSelectionDialog","ok_button","action_area","GtkButton",None),
("GtkColorSelectionDialog","vbox",None,"GtkBox",None),
("GtkComboBox","entry",None,"GtkEntry","has-entry"),
("GtkDialog","action_area","vbox","GtkButtonBox",None),
("GtkDialog","vbox",None,"GtkBox",None),
("GtkFontSelectionDialog","action_area",None,"GtkButtonBox",None),
("GtkFontSelectionDialog","apply_button","action_area","GtkButton",None),
("GtkFontSelectionDialog","cancel_button","action_area","GtkButton",None),
("GtkFontSelectionDialog","font_selection","vbox","GtkFontSelection",None),
("GtkFontSelectionDialog","ok_button","action_area","GtkButton",None),
("GtkFontSelectionDialog","vbox",None,"GtkBox",None),
("GtkInfoBar","action_area",None,"GtkButtonBox",None),
("GtkInfoBar","content_area",None,"GtkBox",None),
("GtkScaleButton","minus_button",None,"GtkButton",None),
("GtkScaleButton","plus_button",None,"GtkButton",None),
("GtkTreeView","selection",None,"GtkTreeSelection",None)
</type_internal_child>
<property>
("CmbAccessibleAction","cmb-a11y-action-activate","gchararray",None,None,None,None,None,None,None,None,1,None,None,None,None,None,None),
("CmbAccessibleAction","cmb-a11y-action-click","gchararray",None,None,None,None,None,None,None,None,1,None,None,None,None,None,None),
@ -1497,7 +1521,7 @@
("GtkAction","visible-overflown","gboolean",None,None,None,"True",None,None,None,"3.10",None,None,None,None,None,None,None),
("GtkAction","visible-vertical","gboolean",None,None,None,"True",None,None,None,"3.10",None,None,None,None,None,None,None),
("GtkActionBarLayoutChild","pack-type","GtkPackType",None,None,None,"start",None,None,None,None,None,None,None,None,None,None,None),
("GtkActionBarLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,1,None,None,None,None),
("GtkActionBarLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,None,None,None,None,None),
("GtkActionGroup","accel-group","GtkAccelGroup",1,None,None,None,None,None,None,"3.10",None,None,None,None,None,None,None),
("GtkActionGroup","name","gchararray",None,1,None,None,None,None,None,"3.10",None,None,None,None,None,None,None),
("GtkActionGroup","sensitive","gboolean",None,None,None,"True",None,None,None,"3.10",None,None,None,None,None,None,None),
@ -1557,7 +1581,7 @@
("GtkBoxLayoutChild","fill","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("GtkBoxLayoutChild","pack-type","GtkPackType",None,None,None,"start",None,None,None,None,None,None,None,None,None,None,None),
("GtkBoxLayoutChild","padding","guint",None,None,None,"0","0","2147483647",None,None,None,None,None,None,None,None,None),
("GtkBoxLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,1,None,None,None,None),
("GtkBoxLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,None,None,None,None,None),
("GtkBuilder","translation-domain","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkButton","always-show-image","gboolean",None,None,None,"False",None,None,"3.6",None,None,None,None,None,None,None,None),
("GtkButton","can-focus","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
@ -1908,7 +1932,7 @@
("GtkHeaderBar","subtitle","gchararray",None,None,None,None,None,None,None,None,1,None,None,None,None,None,None),
("GtkHeaderBar","title","gchararray",None,None,None,None,None,None,None,None,1,None,None,None,None,None,None),
("GtkHeaderBarLayoutChild","pack-type","GtkPackType",None,None,None,"start",None,None,None,None,None,None,None,None,None,None,None),
("GtkHeaderBarLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,1,None,None,None,None),
("GtkHeaderBarLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,None,None,None,None,None),
("GtkIMContext","input-hints","GtkInputHints",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkIMContext","input-purpose","GtkInputPurpose",None,None,None,"free-form",None,None,None,None,None,None,None,None,None,None,None),
("GtkIconView","activate-on-single-click","gboolean",None,None,None,"False",None,None,"3.8",None,None,None,None,None,None,None,None),
@ -2061,7 +2085,7 @@
("GtkNotebook","tab-pos","GtkPositionType",None,None,None,"top",None,None,None,None,None,None,None,None,None,None,None),
("GtkNotebookLayoutChild","detachable","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkNotebookLayoutChild","menu-label","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkNotebookLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,1,None,None,None,None),
("GtkNotebookLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,None,None,None,None,None),
("GtkNotebookLayoutChild","reorderable","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkNotebookLayoutChild","tab-expand","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkNotebookLayoutChild","tab-fill","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
@ -2102,7 +2126,7 @@
("GtkPopover","relative-to","GtkWidget",1,None,None,None,None,None,"3.12",None,None,None,None,None,None,None,None),
("GtkPopover","transitions-enabled","gboolean",None,None,None,"True",None,None,"3.16","3.22",None,None,None,None,None,None,None),
("GtkPopoverMenu","visible-submenu","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkPopoverMenuLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,1,None,None,None,None),
("GtkPopoverMenuLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,None,None,None,None,None),
("GtkPopoverMenuLayoutChild","submenu","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkPrintOperation","allow-async","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkPrintOperation","current-page","gint",None,None,None,"-1","-1","2147483647",None,None,None,None,None,None,None,None,None),
@ -2340,7 +2364,7 @@
("GtkStackLayoutChild","icon-name","CmbIconName",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkStackLayoutChild","name","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkStackLayoutChild","needs-attention","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkStackLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,1,None,None,None,None),
("GtkStackLayoutChild","position","gint",None,None,None,"0","-1","2147483647",None,None,None,None,None,None,None,None,None),
("GtkStackLayoutChild","title","gchararray",None,None,None,None,None,None,None,None,1,None,None,None,None,None,None),
("GtkStackSidebar","stack","GtkStack",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkStackSwitcher","icon-size","gint",None,None,None,"1","0","2147483647","3.20",None,None,None,None,None,None,None,None),
@ -2503,7 +2527,7 @@
("GtkToolItemGroupLayoutChild","fill","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("GtkToolItemGroupLayoutChild","homogeneous","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("GtkToolItemGroupLayoutChild","new-row","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkToolItemGroupLayoutChild","position","gint",None,None,None,"0","0","2147483647",None,None,None,None,1,None,None,None,None),
("GtkToolItemGroupLayoutChild","position","gint",None,None,None,"0","0","2147483647",None,None,None,None,None,None,None,None,None),
("GtkToolPalette","icon-size","GtkIconSize",None,None,None,"small-toolbar",None,None,None,None,None,None,None,None,None,None,None),
("GtkToolPalette","icon-size-set","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkToolPalette","toolbar-style","GtkToolbarStyle",None,None,None,"icons",None,None,None,None,None,None,None,None,None,None,None),
@ -2714,6 +2738,7 @@
("GtkEntry","paste-clipboard",None,None,None),
("GtkEntry","populate-popup",None,None,None),
("GtkEntry","preedit-changed",None,None,None),
("GtkEntry","toggle-direction",None,None,None),
("GtkEntry","toggle-overwrite",None,None,None),
("GtkEntryBuffer","deleted-text",None,None,None),
("GtkEntryBuffer","inserted-text",None,None,None),

View File

@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE cambalache-catalog SYSTEM "cambalache-catalog.dtd">
<cambalache-catalog name="gtk" namespace="Gtk" prefix="Gtk" version="4.0" targets="4.0,4.10,4.12,4.14,4.16,4.2,4.4,4.6,4.8" depends="gdk-4.0,gsk-4.0">
<cambalache-catalog name="gtk" namespace="Gtk" prefix="Gtk" version="4.0" targets="4.0,4.10,4.12,4.14,4.16,4.18,4.2,4.4,4.6,4.8" depends="gdk-4.0,gsk-4.0">
<type>
("CmbAccessibleList","gchararray","gtk",None,None,None,None,None,None,None),
("CmbAccessibleProperty","interface","gtk",None,None,None,None,None,None,None),
@ -337,15 +337,15 @@
("GtkShortcutAction","GObject","gtk",None,None,1,1,None,None,None),
("GtkShortcutActionFlags","flags","gtk",None,None,None,None,None,None,None),
("GtkShortcutController","GtkEventController","gtk",None,None,None,1,"container",None,None),
("GtkShortcutLabel","GtkWidget","gtk",None,None,None,1,"container",None,None),
("GtkShortcutLabel","GtkWidget","gtk",None,"4.18",None,1,"container",None,None),
("GtkShortcutManager","interface","gtk",None,None,None,None,None,None,None),
("GtkShortcutScope","enum","gtk",None,None,None,None,None,None,None),
("GtkShortcutTrigger","GObject","gtk",None,None,1,1,None,None,None),
("GtkShortcutType","enum","gtk",None,None,None,None,None,None,None),
("GtkShortcutsGroup","GtkBox","gtk",None,None,None,1,"container",None,None),
("GtkShortcutsSection","GtkBox","gtk",None,None,None,1,"container",None,None),
("GtkShortcutsShortcut","GtkWidget","gtk",None,None,None,1,"container",None,None),
("GtkShortcutsWindow","GtkWindow","gtk",None,None,None,None,"container",None,None),
("GtkShortcutsGroup","GtkBox","gtk",None,"4.18",None,1,"container",None,None),
("GtkShortcutsSection","GtkBox","gtk",None,"4.18",None,1,"container",None,None),
("GtkShortcutsShortcut","GtkWidget","gtk",None,"4.18",None,1,"container",None,None),
("GtkShortcutsWindow","GtkWindow","gtk",None,"4.18",None,None,"container",None,None),
("GtkSignalAction","GtkShortcutAction","gtk",None,None,None,1,None,None,None),
("GtkSignalListItemFactory","GtkListItemFactory","gtk",None,None,None,1,None,None,None),
("GtkSingleSelection","GObject","gtk",None,None,None,1,None,None,None),
@ -959,7 +959,7 @@
("GtkAccessibleProperty","GTK_ACCESSIBLE_PROPERTY_DESCRIPTION","description",1,"Defines a string value that describes or annotates the current element. Value type: string"),
("GtkAccessibleProperty","GTK_ACCESSIBLE_PROPERTY_HAS_POPUP","has-popup",2,"Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element."),
("GtkAccessibleProperty","GTK_ACCESSIBLE_PROPERTY_HELP_TEXT","help-text",19,"Defines a string value that provides a description of non-standard keyboard interactions of the current element. Value type: string"),
("GtkAccessibleProperty","GTK_ACCESSIBLE_PROPERTY_KEY_SHORTCUTS","key-shortcuts",3,"Indicates keyboard shortcuts that an author has implemented to activate or give focus to an element. Value type: string"),
("GtkAccessibleProperty","GTK_ACCESSIBLE_PROPERTY_KEY_SHORTCUTS","key-shortcuts",3,"Indicates keyboard shortcuts that an author has implemented to activate or give focus to an element. Value type: string. The format of the value is a space-separated list of shortcuts, with each shortcut consisting of one or more modifiers (`Control`, `Alt` or `Shift`), followed by a non-modifier key, all separated by `+`. Examples: `F2`, `Alt-F`, `Control+Shift+N`"),
("GtkAccessibleProperty","GTK_ACCESSIBLE_PROPERTY_LABEL","label",4,"Defines a string value that labels the current element. Value type: string"),
("GtkAccessibleProperty","GTK_ACCESSIBLE_PROPERTY_LEVEL","level",5,"Defines the hierarchical level of an element within a structure. Value type: integer"),
("GtkAccessibleProperty","GTK_ACCESSIBLE_PROPERTY_MODAL","modal",6,"Indicates whether an element is modal when displayed. Value type: boolean"),
@ -980,12 +980,18 @@
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_COL_INDEX","col-index",2,"Defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid. Value type: integer"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT","col-index-text",3,"Defines a human readable text alternative of %GTK_ACCESSIBLE_RELATION_COL_INDEX. Value type: string"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_COL_SPAN","col-span",4,"Defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid. Value type: integer"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_CONTROLLED_BY","controlled-by",20,"Identifies the element (or elements) that the current element is controlled by. Value type: reference This relation is managed by GTK and should not be set from application code."),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_CONTROLS","controls",5,"Identifies the element (or elements) whose contents or presence are controlled by the current element. Value type: reference"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_DESCRIBED_BY","described-by",6,"Identifies the element (or elements) that describes the object. Value type: reference"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_DESCRIPTION_FOR","description-for",19,"Identifies the element (or elements) that are described by the current element. Value type: reference This relation is managed by GTK and should not be set from application code."),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_DETAILS","details",7,"Identifies the element (or elements) that provide additional information related to the object. Value type: reference"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_DETAILS_FOR","details-for",21,"Identifies the element (or elements) for which the current element provides additional information. Value type: reference This relation is managed by GTK and should not be set from application code."),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE","error-message",8,"Identifies the element (or elements) that provide an error message for an object. Value type: reference"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE_FOR","error-message-for",22,"Identifies the element (or elements) for which the current element provides an error message. Value type: reference This relation is managed by GTK and should not be set from application code."),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_FLOW_FROM","flow-from",23,"Identifies the previous element (or elements) in an alternate reading order of content which, at the user's discretion, allows assistive technology to override the general default of reading in document source order. Value type: reference This relation is managed by GTK and should not be set from application code."),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_FLOW_TO","flow-to",9,"Identifies the next element (or elements) in an alternate reading order of content which, at the user's discretion, allows assistive technology to override the general default of reading in document source order. Value type: reference"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_LABELLED_BY","labelled-by",10,"Identifies the element (or elements) that labels the current element. Value type: reference"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_LABEL_FOR","label-for",18,"Identifies the element (or elements) that are labeled by the current element. Value type: reference This relation is managed by GTK and should not be set from application code."),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_OWNS","owns",11,"Identifies an element (or elements) in order to define a visual, functional, or contextual parent/child relationship between elements where the widget hierarchy cannot be used to represent the relationship. Value type: reference"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_POS_IN_SET","pos-in-set",12,"Defines an element's number or position in the current set of listitems or treeitems. Value type: integer"),
("GtkAccessibleRelation","GTK_ACCESSIBLE_RELATION_ROW_COUNT","row-count",13,"Defines the total number of rows in a table, grid, or treegrid. Value type: integer"),
@ -1232,12 +1238,12 @@
("GtkFileChooserError","GTK_FILE_CHOOSER_ERROR_BAD_FILENAME","bad-filename",1,"Indicates a malformed filename."),
("GtkFileChooserError","GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME","incomplete-hostname",3,"Indicates an incomplete hostname (e.g. \"http://foo\" without a slash after that)."),
("GtkFileChooserError","GTK_FILE_CHOOSER_ERROR_NONEXISTENT","nonexistent",None,"Indicates that a file does not exist."),
("GtkFilterChange","GTK_FILTER_CHANGE_DIFFERENT","different",None,"The filter change cannot be described with any of the other enumeration values."),
("GtkFilterChange","GTK_FILTER_CHANGE_LESS_STRICT","less-strict",1,"The filter is less strict than it was before: All items that it used to return %TRUE for still return %TRUE, others now may, too."),
("GtkFilterChange","GTK_FILTER_CHANGE_MORE_STRICT","more-strict",2,"The filter is more strict than it was before: All items that it used to return %FALSE for still return %FALSE, others now may, too."),
("GtkFilterMatch","GTK_FILTER_MATCH_ALL","all",2,"The filter matches all items, gtk_filter_match() will alays return %TRUE."),
("GtkFilterMatch","GTK_FILTER_MATCH_NONE","none",1,"The filter does not match any item, gtk_filter_match() will always return %FALSE."),
("GtkFilterMatch","GTK_FILTER_MATCH_SOME","some",None,"The filter matches some items, gtk_filter_match() may return %TRUE or %FALSE"),
("GtkFilterChange","GTK_FILTER_CHANGE_DIFFERENT","different",None,"The filter change cannot be described with any of the other enumeration values"),
("GtkFilterChange","GTK_FILTER_CHANGE_LESS_STRICT","less-strict",1,"The filter is less strict than it was before: All items that it used to return true still return true, others now may, too."),
("GtkFilterChange","GTK_FILTER_CHANGE_MORE_STRICT","more-strict",2,"The filter is more strict than it was before: All items that it used to return false still return false, others now may, too."),
("GtkFilterMatch","GTK_FILTER_MATCH_ALL","all",2,"The filter matches all items, [method@Gtk.Filter.match] will alays return true"),
("GtkFilterMatch","GTK_FILTER_MATCH_NONE","none",1,"The filter does not match any item, [method@Gtk.Filter.match] will always return false"),
("GtkFilterMatch","GTK_FILTER_MATCH_SOME","some",None,"The filter matches some items, [method@Gtk.Filter.match] may return true or false"),
("GtkFontLevel","GTK_FONT_LEVEL_FACE","face",1,"Select a font face (i.e. a family and a style)"),
("GtkFontLevel","GTK_FONT_LEVEL_FAMILY","family",None,"Select a font family"),
("GtkFontLevel","GTK_FONT_LEVEL_FEATURES","features",3,"Select a font and font features"),
@ -1396,7 +1402,7 @@
("GtkPrintStatus","GTK_PRINT_STATUS_PRINTING","printing",6,"The printer is processing the print job."),
("GtkPrintStatus","GTK_PRINT_STATUS_SENDING_DATA","sending-data",3,"The print job is being sent off to the printer."),
("GtkPropagationLimit","GTK_LIMIT_NONE","none",None,"Events are handled regardless of what their target is."),
("GtkPropagationLimit","GTK_LIMIT_SAME_NATIVE","same-native",1,"Events are only handled if their target is in the same [iface@Native] as the event controllers widget. Note that some event types have two targets (origin and destination)."),
("GtkPropagationLimit","GTK_LIMIT_SAME_NATIVE","same-native",1,"Events are only handled if their target is in the same [iface@Native] (or widget with [property@Gtk.Widget:limit-events] set) as the event controllers widget. Note that some event types have two targets (origin and destination)."),
("GtkPropagationPhase","GTK_PHASE_BUBBLE","bubble",2,"Events are delivered in the bubble phase. The bubble phase happens after the capture phase, and before the default handlers are run. This phase runs from the event widget, up to the toplevel."),
("GtkPropagationPhase","GTK_PHASE_CAPTURE","capture",1,"Events are delivered in the capture phase. The capture phase happens before the bubble phase, runs from the toplevel down to the event widget. This option should only be used on containers that might possibly handle events before their children do."),
("GtkPropagationPhase","GTK_PHASE_NONE","none",None,"Events are not delivered."),
@ -1521,9 +1527,9 @@
("GtkStackTransitionType","GTK_STACK_TRANSITION_TYPE_UNDER_LEFT","under-left",14,"Uncover the new page by sliding to the left"),
("GtkStackTransitionType","GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT","under-right",15,"Uncover the new page by sliding to the right"),
("GtkStackTransitionType","GTK_STACK_TRANSITION_TYPE_UNDER_UP","under-up",12,"Uncover the new page by sliding up"),
("GtkStringFilterMatchMode","GTK_STRING_FILTER_MATCH_MODE_EXACT","exact",None,"The search string and text must match exactly."),
("GtkStringFilterMatchMode","GTK_STRING_FILTER_MATCH_MODE_PREFIX","prefix",2,"The text must begin with the search string."),
("GtkStringFilterMatchMode","GTK_STRING_FILTER_MATCH_MODE_SUBSTRING","substring",1,"The search string must be contained as a substring inside the text."),
("GtkStringFilterMatchMode","GTK_STRING_FILTER_MATCH_MODE_EXACT","exact",None,"The search string and text must match exactly"),
("GtkStringFilterMatchMode","GTK_STRING_FILTER_MATCH_MODE_PREFIX","prefix",2,"The text must begin with the search string"),
("GtkStringFilterMatchMode","GTK_STRING_FILTER_MATCH_MODE_SUBSTRING","substring",1,"The search string must be contained as a substring inside the text"),
("GtkSymbolicColor","GTK_SYMBOLIC_COLOR_ERROR","error",1,"Indication color for errors"),
("GtkSymbolicColor","GTK_SYMBOLIC_COLOR_FOREGROUND","foreground",None,"The default foreground color"),
("GtkSymbolicColor","GTK_SYMBOLIC_COLOR_SUCCESS","success",3,"Indication color for success"),
@ -1581,8 +1587,9 @@
("GtkCellRendererState","GTK_CELL_RENDERER_SORTED","sorted",8,"The cell is in a sorted row"),
("GtkDebugFlags","GTK_DEBUG_A11Y","a11y",131072,"Information about accessibility state changes"),
("GtkDebugFlags","GTK_DEBUG_ACTIONS","actions",4096,"Information about actions and menu models"),
("GtkDebugFlags","GTK_DEBUG_BUILDER","builder",128,"Trace GtkBuilder operation"),
("GtkDebugFlags","GTK_DEBUG_BUILDER","builder",2097152,"Trace GtkBuilder operation"),
("GtkDebugFlags","GTK_DEBUG_BUILDER_OBJECTS","builder-objects",65536,"Log unused GtkBuilder objects"),
("GtkDebugFlags","GTK_DEBUG_BUILDER_TRACE","builder-trace",128,None),
("GtkDebugFlags","GTK_DEBUG_CONSTRAINTS","constraints",32768,"Information from the constraints solver"),
("GtkDebugFlags","GTK_DEBUG_CSS","css",1048576,"Information about deprecated CSS features."),
("GtkDebugFlags","GTK_DEBUG_GEOMETRY","geometry",16,"Information about size allocation"),
@ -1615,6 +1622,7 @@
("GtkFontChooserLevel","GTK_FONT_CHOOSER_LEVEL_VARIATIONS","variations",4,"Allow changing OpenType font variation axes"),
("GtkIconLookupFlags","GTK_ICON_LOOKUP_FORCE_REGULAR","force-regular",1,"Try to always load regular icons, even when symbolic icon names are given"),
("GtkIconLookupFlags","GTK_ICON_LOOKUP_FORCE_SYMBOLIC","force-symbolic",2,"Try to always load symbolic icons, even when regular icon names are given"),
("GtkIconLookupFlags","GTK_ICON_LOOKUP_NONE","none",None,"Perform a regular lookup."),
("GtkIconLookupFlags","GTK_ICON_LOOKUP_PRELOAD","preload",4,"Starts loading the texture in the background so it is ready when later needed."),
("GtkInputHints","GTK_INPUT_HINT_EMOJI","emoji",512,"Suggest offering Emoji support"),
("GtkInputHints","GTK_INPUT_HINT_INHIBIT_OSK","inhibit-osk",128,"Suggest to not show an onscreen keyboard (e.g for a calculator that already has all the keys)."),
@ -1680,7 +1688,7 @@
</type_flags>
<type_data>
("GtkComboBoxText",1,None,"items",None,None),
("GtkComboBoxText",2,1,"item","gchararray",None),
("GtkComboBoxText",2,1,"item","gchararray",1),
("GtkDialog",1,None,"action-widgets",None,None),
("GtkDialog",2,1,"action-widget","GtkWidget",None),
("GtkFileFilter",1,None,"mime-types",None,None),
@ -1733,6 +1741,7 @@
("GtkCenterBox","center",1,None),
("GtkCenterBox","end",1,None),
("GtkCenterBox","start",1,None),
("GtkDialog","action",None,None),
("GtkExpander","label",1,None),
("GtkFrame","label",1,None),
("GtkHeaderBar","end",None,None),
@ -1748,6 +1757,14 @@
<type_child_constraint>
("GtkStack","GtkStackPage",1,1)
</type_child_constraint>
<type_internal_child>
("GtkAssistant","action_area",None,"GtkBox",None),
("GtkComboBox","entry",None,"GtkEntry","has-entry"),
("GtkDialog","action_area",None,"GtkBox",None),
("GtkScaleButton","minus_button",None,"GtkButton",None),
("GtkScaleButton","plus_button",None,"GtkButton",None),
("GtkTreeView","selection",None,"GtkTreeSelection",None)
</type_internal_child>
<property>
("CmbAccessibleProperty","cmb-a11y-property-autocomplete","GtkAccessibleAutocomplete",None,None,None,"none",None,None,None,None,None,None,None,None,None,None,None),
("CmbAccessibleProperty","cmb-a11y-property-description","gchararray",None,None,None,None,None,None,None,None,1,None,None,None,None,None,None),
@ -2246,6 +2263,8 @@
("GtkFontChooser","level","GtkFontChooserLevel",None,None,None,"style | size",None,None,None,"4.10",None,None,None,None,None,None,None),
("GtkFontChooser","preview-text","gchararray",None,None,None,"The quick brown fox jumps over the lazy dog.",None,None,None,"4.10",None,None,None,None,None,None,None),
("GtkFontChooser","show-preview-entry","gboolean",None,None,None,"True",None,None,None,"4.10",None,None,None,None,None,None,None),
("GtkFontChooserDialog","default-height","gint",None,None,None,"500","-1","2147483647",None,None,None,None,None,None,None,"GtkWindow",None),
("GtkFontChooserDialog","default-width","gint",None,None,None,"400","-1","2147483647",None,None,None,None,None,None,None,"GtkWindow",None),
("GtkFontChooserDialog","title","gchararray",None,None,None,"Select Font",None,None,None,None,None,None,None,None,None,"GtkWindow",None),
("GtkFontChooserDialog","use-header-bar","gint",None,1,None,"1","-1","1",None,"4.10",None,None,None,None,None,"GtkDialog",None),
("GtkFontChooserWidget","css-name","gchararray",None,1,None,"fontchooser",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
@ -2313,6 +2332,7 @@
("GtkHeaderBar","decoration-layout","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkHeaderBar","show-title-buttons","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("GtkHeaderBar","title-widget","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkHeaderBar","use-native-controls","gboolean",None,None,None,"False",None,None,"4.18",None,None,None,None,None,None,None,None),
("GtkIMContext","input-hints","GtkInputHints",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkIMContext","input-purpose","GtkInputPurpose",None,None,None,"free-form",None,None,None,None,None,None,None,None,None,None,None),
("GtkIconPaintable","file","GFile",None,1,None,None,None,None,None,None,None,None,None,None,None,None,None),
@ -2410,6 +2430,7 @@
("GtkListBox","focusable","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
("GtkListBox","selection-mode","GtkSelectionMode",None,None,None,"single",None,None,None,None,None,None,None,None,None,None,None),
("GtkListBox","show-separators","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkListBox","tab-behavior","GtkListTabBehavior",None,None,None,"all",None,None,"4.18",None,None,None,None,None,None,None,None),
("GtkListBoxRow","accessible-role","GtkAccessibleRole",None,None,None,"list-item",None,None,None,None,None,None,None,None,None,"GtkAccessible",None),
("GtkListBoxRow","activatable","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("GtkListBoxRow","child","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
@ -2725,42 +2746,42 @@
("GtkShortcutController","mnemonic-modifiers","GdkModifierType",None,None,None,"alt-mask",None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutController","model","GListModel",1,1,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutController","scope","GtkShortcutScope",None,None,None,"local",None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutLabel","accelerator","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutLabel","accelerator","gchararray",None,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutLabel","accessible-role","GtkAccessibleRole",None,None,None,"group",None,None,None,None,None,None,None,None,None,"GtkAccessible",None),
("GtkShortcutLabel","css-name","gchararray",None,1,None,"shortcut",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
("GtkShortcutLabel","disabled-text","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsGroup","accel-size-group","GtkSizeGroup",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutLabel","disabled-text","gchararray",None,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsGroup","accel-size-group","GtkSizeGroup",1,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsGroup","accessible-role","GtkAccessibleRole",None,None,None,"group",None,None,None,None,None,None,None,None,None,"GtkAccessible",None),
("GtkShortcutsGroup","css-name","gchararray",None,1,None,"shortcuts-group",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
("GtkShortcutsGroup","orientation","GtkOrientation",None,None,None,"vertical",None,None,None,None,None,None,None,None,None,"GtkOrientable",None),
("GtkShortcutsGroup","spacing","gint",None,None,None,"10","0","2147483647",None,None,None,None,None,None,None,"GtkBox",None),
("GtkShortcutsGroup","title","gchararray",None,None,None,"",None,None,None,None,1,None,None,None,None,None,None),
("GtkShortcutsGroup","title-size-group","GtkSizeGroup",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsGroup","view","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsGroup","title","gchararray",None,None,None,"",None,None,None,"4.18",1,None,None,None,None,None,None),
("GtkShortcutsGroup","title-size-group","GtkSizeGroup",1,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsGroup","view","gchararray",None,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsSection","css-name","gchararray",None,1,None,"shortcuts-section",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
("GtkShortcutsSection","max-height","guint",None,None,None,"15","0","4294967295",None,None,None,None,None,None,None,None,None),
("GtkShortcutsSection","max-height","guint",None,None,None,"15","0","4294967295",None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsSection","orientation","GtkOrientation",None,None,None,"vertical",None,None,None,None,None,None,None,None,None,"GtkOrientable",None),
("GtkShortcutsSection","section-name","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsSection","section-name","gchararray",None,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsSection","spacing","gint",None,None,None,"22","0","2147483647",None,None,None,None,None,None,None,"GtkBox",None),
("GtkShortcutsSection","title","gchararray",None,None,None,None,None,None,None,None,1,None,None,None,None,None,None),
("GtkShortcutsSection","view-name","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsShortcut","accel-size-group","GtkSizeGroup",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsShortcut","accelerator","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsSection","title","gchararray",None,None,None,None,None,None,None,"4.18",1,None,None,None,None,None,None),
("GtkShortcutsSection","view-name","gchararray",None,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsShortcut","accel-size-group","GtkSizeGroup",1,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsShortcut","accelerator","gchararray",None,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsShortcut","accessible-role","GtkAccessibleRole",None,None,None,"label",None,None,None,None,None,None,None,None,None,"GtkAccessible",None),
("GtkShortcutsShortcut","action-name","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsShortcut","action-name","gchararray",None,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsShortcut","css-name","gchararray",None,1,None,"shortcut",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
("GtkShortcutsShortcut","direction","GtkTextDirection",None,None,None,"none",None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsShortcut","direction","GtkTextDirection",None,None,None,"none",None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsShortcut","focusable","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
("GtkShortcutsShortcut","icon-set","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsShortcut","shortcut-type","GtkShortcutType",None,None,None,"accelerator",None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsShortcut","subtitle","gchararray",None,None,None,"",None,None,None,None,1,None,None,None,None,None,None),
("GtkShortcutsShortcut","subtitle-set","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsShortcut","title","gchararray",None,None,None,"",None,None,None,None,1,None,None,None,None,None,None),
("GtkShortcutsShortcut","title-size-group","GtkSizeGroup",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsShortcut","icon-set","gboolean",None,None,None,"False",None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsShortcut","shortcut-type","GtkShortcutType",None,None,None,"accelerator",None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsShortcut","subtitle","gchararray",None,None,None,"",None,None,None,"4.18",1,None,None,None,None,None,None),
("GtkShortcutsShortcut","subtitle-set","gboolean",None,None,None,"False",None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsShortcut","title","gchararray",None,None,None,"",None,None,None,"4.18",1,None,None,None,None,None,None),
("GtkShortcutsShortcut","title-size-group","GtkSizeGroup",1,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsWindow","accessible-role","GtkAccessibleRole",None,None,None,"generic",None,None,None,None,None,None,None,None,None,"GtkAccessible",None),
("GtkShortcutsWindow","resizable","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,"GtkWindow",None),
("GtkShortcutsWindow","section-name","gchararray",None,None,None,"internal-search",None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsWindow","view-name","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkShortcutsWindow","section-name","gchararray",None,None,None,"internal-search",None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkShortcutsWindow","view-name","gchararray",None,None,None,None,None,None,None,"4.18",None,None,None,None,None,None,None),
("GtkSignalAction","signal-name","gchararray",None,1,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkSingleSelection","autoselect","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("GtkSingleSelection","can-unselect","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
@ -3034,6 +3055,7 @@
("GtkWidget","hexpand","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkWidget","hexpand-set","gboolean",None,None,None,"False",None,None,None,None,None,None,None,None,None,None,None),
("GtkWidget","layout-manager","GtkLayoutManager",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkWidget","limit-events","gboolean",None,None,None,"False",None,None,"4.18",None,None,None,None,None,None,None,None),
("GtkWidget","margin-bottom","gint",None,None,None,"0","0","32767",None,None,None,None,None,None,None,None,None),
("GtkWidget","margin-end","gint",None,None,None,"0","0","32767",None,None,None,None,None,None,None,None,None),
("GtkWidget","margin-start","gint",None,None,None,"0","0","32767",None,None,None,None,None,None,None,None,None),
@ -3051,7 +3073,7 @@
("GtkWidget","visible","gboolean",None,None,None,"True",None,None,None,None,None,None,None,None,None,None,None),
("GtkWidget","width-request","gint",None,None,None,"-1","-1","2147483647",None,None,None,None,None,None,None,None,None),
("GtkWidgetPaintable","widget","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkWindow","accessible-role","GtkAccessibleRole",None,None,None,"application",None,None,None,None,None,None,None,None,None,"GtkAccessible",None),
("GtkWindow","accessible-role","GtkAccessibleRole",None,None,None,"window",None,None,None,None,None,None,None,None,None,"GtkAccessible",None),
("GtkWindow","application","GtkApplication",1,None,None,None,None,None,None,None,None,1,None,None,None,None,None),
("GtkWindow","child","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkWindow","css-name","gchararray",None,1,None,"window",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
@ -3082,6 +3104,7 @@
("GtkWindowControls","css-name","gchararray",None,1,None,"windowcontrols",None,None,None,None,None,None,None,None,None,"GtkWidget",None),
("GtkWindowControls","decoration-layout","gchararray",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkWindowControls","side","GtkPackType",None,None,None,"start",None,None,None,None,None,None,None,None,None,None,None),
("GtkWindowControls","use-native-controls","gboolean",None,None,None,"False",None,None,"4.18",None,None,None,None,None,None,None,None),
("GtkWindowHandle","child","GtkWidget",1,None,None,None,None,None,None,None,None,None,None,None,None,None,None),
("GtkWindowHandle","css-name","gchararray",None,1,None,"windowhandle",None,None,None,None,None,None,None,None,None,"GtkWidget",None)
</property>
@ -3327,9 +3350,9 @@
("GtkSearchEntry","stop-search",None,None,None),
("GtkSectionModel","sections-changed","4.12",None,None),
("GtkSelectionModel","selection-changed",None,None,None),
("GtkShortcutsSection","change-current-page",None,None,None),
("GtkShortcutsWindow","close",None,None,None),
("GtkShortcutsWindow","search",None,None,None),
("GtkShortcutsSection","change-current-page",None,"4.18",None),
("GtkShortcutsWindow","close",None,"4.18",None),
("GtkShortcutsWindow","search",None,"4.18",None),
("GtkSignalListItemFactory","bind",None,None,None),
("GtkSignalListItemFactory","setup",None,None,None),
("GtkSignalListItemFactory","teardown",None,None,None),

View File

@ -1,18 +1,22 @@
install_data([
'atk/atk-1.0.xml',
'glib/gobject-2.0.xml',
'glib/gio-2.0.xml',
'gdkpixbuf/gdkpixbuf-2.0.xml',
'pango/pango-1.0.xml',
'gtk/gdk-3.0.xml',
'gtk/gdk-4.0.xml',
'gtk/gsk-4.0.xml',
'gtk/gtk-4.0.xml',
'gtk/gtk+-3.0.xml',
'gnome/webkit2gtk-4.1.xml',
'gnome/webkitgtk-6.0.xml',
'gnome/libhandy-1.xml',
'gnome/libadwaita-1.xml'
],
install_dir: catalogsdir)
foreach data: [
[gtk3_dep, ['atk/atk-1.0.xml', 'gtk/gdk-3.0.xml', 'gtk/gtk+-3.0.xml']],
[handy_dep, ['gnome/libhandy-1.xml']],
[webkit_dep, ['gnome/webkitgtk-6.0.xml']],
[webkit2_dep, ['gnome/webkit2gtk-4.1.xml']],
]
if data[0].found()
install_data(data[1], install_dir: catalogsdir)
endif
endforeach

View File

@ -301,17 +301,19 @@
("PangoWeight","PANGO_WEIGHT_ULTRAHEAVY","ultraheavy",1000,"the ultraheavy weight (= 1000) Since: 1.24"),
("PangoWeight","PANGO_WEIGHT_ULTRALIGHT","ultralight",200,"the ultralight weight (= 200)"),
("PangoWrapMode","PANGO_WRAP_CHAR","char",1,"wrap lines at character boundaries."),
("PangoWrapMode","PANGO_WRAP_NONE","none",3,"do not wrap."),
("PangoWrapMode","PANGO_WRAP_WORD","word",None,"wrap lines at word boundaries."),
("PangoWrapMode","PANGO_WRAP_WORD_CHAR","word-char",2,"wrap lines at word boundaries, but fall back to character boundaries if there is not enough space for a full word.")
</type_enum>
<type_flags>
("PangoFontMask","PANGO_FONT_MASK_FAMILY","family",1,"the font family is specified."),
("PangoFontMask","PANGO_FONT_MASK_GRAVITY","gravity",64,"the font gravity is specified (Since: 1.16.)"),
("PangoFontMask","PANGO_FONT_MASK_FEATURES","features",256,"OpenType font features are specified."),
("PangoFontMask","PANGO_FONT_MASK_GRAVITY","gravity",64,"The font gravity is specified."),
("PangoFontMask","PANGO_FONT_MASK_SIZE","size",32,"the font size is specified."),
("PangoFontMask","PANGO_FONT_MASK_STRETCH","stretch",16,"the font stretch is specified."),
("PangoFontMask","PANGO_FONT_MASK_STYLE","style",2,"the font style is specified."),
("PangoFontMask","PANGO_FONT_MASK_VARIANT","variant",4,"the font variant is specified."),
("PangoFontMask","PANGO_FONT_MASK_VARIATIONS","variations",128,"OpenType font variations are specified (Since: 1.42)"),
("PangoFontMask","PANGO_FONT_MASK_VARIATIONS","variations",128,"OpenType font variations are specified."),
("PangoFontMask","PANGO_FONT_MASK_WEIGHT","weight",8,"the font weight is specified."),
("PangoLayoutDeserializeFlags","PANGO_LAYOUT_DESERIALIZE_CONTEXT","context",1,"Apply context information from the serialization to the `PangoContext`"),
("PangoLayoutDeserializeFlags","PANGO_LAYOUT_DESERIALIZE_DEFAULT","default",None,"Default behavior"),

View File

@ -1,14 +1,14 @@
#!/bin/bash
BASEDIR=`realpath $(dirname "$0")`
SCRIPT=$(readlink -f $0)
DIRNAME=$(dirname $SCRIPT)
source .local.env
export COVERAGE_PROCESS_START=$BASEDIR/pyproject.toml
export COVERAGE_PROCESS_START=$DIRNAME/pyproject.toml
#Run tests with coverage if available
if command -v python3-coverage &> /dev/null
then
python3-coverage run --rcfile=$BASEDIR/../pyproject.toml -m pytest $@
python3-coverage run --rcfile=$DIRNAME/pyproject.toml -m pytest $@
python3-coverage combine
python3-coverage report -m
else

Some files were not shown because too many files have changed in this diff Show More