Compare commits

...

2 Commits

Author SHA1 Message Date
Juan Pablo Ugarte
fa89006ff2 Super massive commit to port to Gtk 4
Fix issue #40 "Update GUI to GTK 4"
2024-02-06 17:08:19 -05:00
Juan Pablo Ugarte
15b8ecf3d8 CmbDB: add support to load properties and signals from project. 2024-02-04 22:32:02 -05:00
44 changed files with 1726 additions and 2691 deletions

View File

@ -1,6 +1,6 @@
# Cambalache
#
# Copyright (C) 2021 Juan Pablo Ugarte
# 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
@ -28,10 +28,10 @@ import builtins
from . import config
gi.require_version("Gdk", "3.0")
gi.require_version("Gtk", "3.0")
gi.require_version("GtkSource", "4")
gi.require_version("WebKit2", "4.1")
gi.require_version("Gdk", "4.0")
gi.require_version("Gtk", "4.0")
gi.require_version("GtkSource", "5")
gi.require_version("WebKit", "6.0")
# Ensure _() builtin
if "_" not in builtins.__dict__:
@ -54,8 +54,9 @@ resource._register()
provider = Gtk.CssProvider()
provider.load_from_resource("/ar/xjuan/Cambalache/cambalache.css")
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
Gtk.IconTheme.get_default().add_resource_path("/ar/xjuan/Cambalache/icons")
display = Gdk.Display.get_default()
Gtk.StyleContext.add_provider_for_display(display, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
Gtk.IconTheme.get_for_display(display).add_resource_path("/ar/xjuan/Cambalache/icons")
def getLogger(name):

View File

@ -21,27 +21,14 @@
*
*/
@binding-set WindowBindings {
bind "<Control>s" { "cmb-action" ("save") };
bind "<Control>w" { "cmb-action" ("close") };
bind "<Control>z" { "cmb-action" ("undo") };
bind "<Control><shift>z" { "cmb-action" ("redo") };
bind "Delete" { "cmb-action" ("delete") };
bind "<Control>n" { "cmb-action" ("create_new") };
bind "<Control>o" { "cmb-action" ("open") };
bind "<Control>Insert" { "cmb-action-bool" ("add_placeholder", 0) };
bind "<Control>Delete" { "cmb-action-bool" ("remove_placeholder", 0) };
bind "<Control><shift>Insert" { "cmb-action-bool" ("add_placeholder", 1) };
bind "<Control><shift>Delete" { "cmb-action-bool" ("remove_placeholder", 1) };
}
CmbWindow .logo {
background: url('resource:///ar/xjuan/Cambalache/app/images/logo-symbolic.svg') no-repeat 50% 35% / 40%
background: url('resource:///ar/xjuan/Cambalache/app/images/logo-symbolic.svg') no-repeat 50% 35% / 40%;
}
CmbWindow.dark .logo {
color: white;
background: -gtk-recolor(url('resource:///ar/xjuan/Cambalache/app/images/logo-symbolic.svg'), success #ffcb85, error #1a1a1a) no-repeat 50% 35% / 40%
background: -gtk-recolor(url('resource:///ar/xjuan/Cambalache/app/images/logo-symbolic.svg'), success #ffcb85, error #1a1a1a) no-repeat 50% 35% / 40%;
}
CmbWindow label.message {
@ -56,7 +43,7 @@ CmbWindow.dark label.message {
background-color: rgba(255, 255, 255, .6);
}
popover.cmb-tutor {
popover.cmb-tutor > * {
padding: 1em;
}
@ -65,13 +52,23 @@ popover.cmb-tutor label {
font-weight: bold;
}
popover.cmb-tutor image {
padding-right: 1em;
-gtk-icon-size: 48px;
}
button.cmb-tutor-highlight,
modelbutton.cmb-tutor-highlight,
buttonbox.cmb-tutor-highlight > button,
stackswitcher.cmb-tutor-highlight > button,
entry.cmb-tutor-highlight,
treeview.cmb-tutor-highlight,
box.cmb-tutor-highlight {
box.cmb-tutor-highlight,
CmbView.cmb-tutor-highlight {
box-shadow: inset 0px 0px 6px @theme_selected_bg_color;
transition: box-shadow .75s ease;
}
CmbView.cmb-tutor-highlight {
padding: 6px;
}

View File

@ -1,7 +1,7 @@
#
# Cambalache Application
#
# Copyright (C) 2021 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
@ -43,7 +43,8 @@ class CmbApplication(Gtk.Application):
def __add_window(self):
window = CmbWindow(application=self)
window.connect("open-project", self.__on_open_project)
window.connect("delete-event", self.__on_window_delete_event)
window.connect("close-request", self.__on_window_close_request)
self.add_window(window)
return window
@ -91,7 +92,7 @@ class CmbApplication(Gtk.Application):
provider = Gtk.CssProvider()
provider.load_from_resource("/ar/xjuan/Cambalache/app/cambalache.css")
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
def do_activate(self):
if self.props.active_window is None:
@ -103,9 +104,17 @@ class CmbApplication(Gtk.Application):
else:
self.open_project(filename, target_tk, uiname)
def __check_can_quit(self, windows):
def __check_can_quit(self, window=None):
windows = self.__get_windows() if window is None else [window]
unsaved_windows = []
projects2save = []
windows2save = []
def do_quit():
if window is None:
self.quit()
else:
self.remove_window(window)
window.destroy()
# Gather projects that needs saving
for win in windows:
@ -117,16 +126,17 @@ class CmbApplication(Gtk.Application):
unsaved_windows_len = len(unsaved_windows)
if unsaved_windows_len == 0:
return True
do_quit()
return
# Create Dialog
text = _("Save changes before closing?")
dialog = Gtk.MessageDialog(
transient_for=windows[0],
flags=0,
message_type=Gtk.MessageType.QUESTION,
text=f"<b><big>{text}</big></b>",
use_markup=True,
modal=True
)
# Add buttons
@ -141,36 +151,58 @@ class CmbApplication(Gtk.Application):
dialog.set_default_response(Gtk.ResponseType.ACCEPT)
if unsaved_windows_len > 1:
if unsaved_windows_len > 1 or unsaved_windows[0].project.filename is None:
# Add checkbox for each unsaved project
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
box.add(Gtk.Label(label=_("Select which files:"), halign=Gtk.Align.START))
box.append(Gtk.Label(label=_("Select which files:"), halign=Gtk.Align.START))
home = GLib.get_home_dir()
untitled = 0
for win in unsaved_windows:
path = win.project.filename.replace(GLib.get_home_dir(), "~")
check = Gtk.CheckButton(label=path, active=True, margin_start=8, can_focus=False)
projects2save.append((win.project, check))
box.add(check)
if win.project.filename is None:
untitled += 1
box.show_all()
dialog.props.message_area.add(box)
# Find Unique name
while os.path.exists(f"Untitled {untitled}.cmb"):
untitled += 1
# Run Dialog
response = dialog.run()
dialog.destroy()
check = Gtk.CheckButton(active=True, margin_start=8, can_focus=False)
entry = Gtk.Entry(text=f"Untitled {untitled}")
# Handle response
if response == Gtk.ResponseType.ACCEPT:
if unsaved_windows_len > 1:
for project, check in projects2save:
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
hbox.append(check)
hbox.append(entry)
box.append(hbox)
else:
path = win.project.filename.replace(home, "~")
check = Gtk.CheckButton(label=path, active=True, margin_start=8, can_focus=False)
box.append(check)
windows2save.append((win, check, entry))
box.show()
dialog.props.message_area.append(box)
else:
windows2save.append((unsaved_windows[0], None, None))
def callback(dialog, response, window):
dialog.destroy()
if response == Gtk.ResponseType.ACCEPT:
for win, check, entry in windows2save:
if entry is not None:
win.project.filename = entry.props.text
if check is None or check.props.active:
project.save()
elif unsaved_windows_len:
unsaved_windows[0].project.save()
elif response == Gtk.ResponseType.CANCEL:
return False
win.save_project()
elif response == Gtk.ResponseType.CANCEL:
return
return True
do_quit()
dialog.connect("response", callback, window)
dialog.present()
def __get_windows(self):
retval = []
@ -181,8 +213,9 @@ class CmbApplication(Gtk.Application):
return retval
def __on_window_delete_event(self, window, event):
return not self.__check_can_quit([window])
def __on_window_close_request(self, window):
self.__check_can_quit(window)
return True
def do_window_removed(self, window):
windows = self.__get_windows()
@ -191,8 +224,7 @@ class CmbApplication(Gtk.Application):
self.activate_action("quit")
def _on_quit_activate(self, action, data):
if self.__check_can_quit(self.__get_windows()):
self.quit()
self.__check_can_quit()
def do_handle_local_options(self, options):
if options.contains("version"):

View File

@ -1,86 +1,74 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.9.0 -->
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-name cmb_shortcuts.ui -->
<requires lib="gtk+" version="3.0"/>
<requires lib="gtk" version="4.0"/>
<object class="GtkShortcutsWindow" id="shortcuts">
<property name="section-name">shortcuts</property>
<child>
<object class="GtkShortcutsSection">
<property name="section-name">shortcuts</property>
<property name="visible">True</property>
<child>
<object class="GtkShortcutsGroup">
<property name="title" translatable="yes">Project</property>
<property name="title" translatable="1">Project</property>
<property name="view">shortcuts</property>
<property name="visible">True</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;n</property>
<property name="title" translatable="yes">Create new project</property>
<property name="visible">True</property>
<property name="title" translatable="1">Create new project</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;o</property>
<property name="title" translatable="yes">Open a project</property>
<property name="visible">True</property>
<property name="title" translatable="1">Open a project</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;w</property>
<property name="title" translatable="yes">Close the project</property>
<property name="visible">True</property>
<property name="title" translatable="1">Close the project</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;s</property>
<property name="title" translatable="yes">Save the project</property>
<property name="visible">True</property>
<property name="title" translatable="1">Save the project</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="visible">0</property>
<property name="accelerator">&lt;Control&gt;e</property>
<property name="title" translatable="yes">Save and Export</property>
<property name="title" translatable="1">Save and Export</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkShortcutsGroup">
<property name="title" translatable="yes">Workspace</property>
<property name="title" translatable="1">Workspace</property>
<property name="view">shortcuts</property>
<property name="visible">True</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;Insert</property>
<property name="title" translatable="yes">Add slot/column</property>
<property name="visible">True</property>
<property name="title" translatable="1">Add slot/column</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;Delete</property>
<property name="title" translatable="yes">Remove slot/column</property>
<property name="visible">True</property>
<property name="title" translatable="1">Remove slot/column</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;&lt;shift&gt;Insert</property>
<property name="title" translatable="yes">Add row</property>
<property name="visible">True</property>
<property name="title" translatable="1">Add row</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="accelerator">&lt;Control&gt;&lt;shift&gt;Delete</property>
<property name="title" translatable="yes">Remove row</property>
<property name="visible">True</property>
<property name="title" translatable="1">Remove row</property>
</object>
</child>
</object>

View File

@ -26,6 +26,7 @@
from gi.repository import GObject, GLib, Gdk, Gtk
from enum import Enum
from collections import namedtuple
from cambalache import utils
class CmbTutorState(Enum):
@ -85,32 +86,30 @@ class CmbTutor(GObject.GObject):
def __add(self, text, widget_name, delay, name=None, position=CmbTutorPosition.BOTTOM):
retval = {}
def find_widget(w, data):
if data.get("widget", None):
return
def find_by_css_name_or_buildable_id(widget, name):
retval = None
css_name = widget.get_name()
name = None
n = w.get_name()
# Get css name first
if css_name and css_name != GObject.type_name(widget) and css_name == name:
return widget
# Get css name first then GtkBuildable name
if n and n != GObject.type_name(w):
name = n
elif isinstance(w, Gtk.Buildable):
n = Gtk.Buildable.get_name(w)
if n and not n.startswith("___object"):
name = n
# then GtkBuildable name
if isinstance(widget, Gtk.Buildable) and Gtk.Buildable.get_buildable_id(widget) == name:
return widget
# Return widget
if name == widget_name:
data["widget"] = w
return
# or ModelButton name
if GObject.type_name(widget) == "GtkModelButton" and widget.props.text == name:
return widget
if isinstance(w, Gtk.Container):
w.forall(find_widget, data)
for child in utils.widget_get_children(widget):
retval = find_by_css_name_or_buildable_id(child, name)
if retval:
return retval
self.window.forall(find_widget, retval)
return retval
widget = retval.get("widget", None)
widget = find_by_css_name_or_buildable_id(self.window, widget_name)
if widget:
self.script.append(ScriptNode(widget, text, delay, name, position))
@ -140,14 +139,19 @@ class CmbTutor(GObject.GObject):
self.notify("state")
def __popover_new(self, text):
popover = Gtk.Popover(modal=False)
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
popover = Gtk.Popover(autohide=False)
popover.add_css_class("cmb-tutor")
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, hexpand=True)
box.add(Gtk.Image(icon_name="dialog-information-symbolic", icon_size=Gtk.IconSize.DIALOG))
box.add(Gtk.Label(label=text, wrap=True, max_width_chars=28))
popover.add(box)
popover.get_style_context().add_class("cmb-tutor")
box.show_all()
box.append(Gtk.Image(icon_name="dialog-information-symbolic"))
box.append(Gtk.Label(
label=text,
vexpand=False,
hexpand=True,
wrap=True,
max_width_chars=24
))
popover.set_child(box)
return popover
@ -203,11 +207,11 @@ class CmbTutor(GObject.GObject):
if parent:
parent.popup()
node.widget.get_style_context().add_class("cmb-tutor-highlight")
node.widget.add_css_class("cmb-tutor-highlight")
# Create popover
self.popover = self.__popover_new(node.text)
self.popover.set_relative_to(node.widget)
self.popover.set_parent(node.widget)
if node.position == CmbTutorPosition.BOTTOM:
self.popover.set_position(Gtk.PositionType.BOTTOM)

View File

@ -36,9 +36,9 @@ intro = [
(_("Common actions like Undo"), "undo_button", 4),
(_("Redo"), "redo_button", 2),
(_("Add new UI file"), "add_button", 3),
(_("and Save are directly accessible in the headerbar"), "save_button", 6),
(_("and Save are directly accessible in the headerbar"), "cmb_save_button", 6),
(_("just like Save As"), "save_as_button", 2),
(_("and the main menu"), "menu_button", 3),
(_("and the main menu"), "menu_button", 3, "menu_button"),
(_("Create a project to continue"), "intro_button", 2, "add-project"),
(_("Great!"), "intro_button", 2),
(
@ -66,14 +66,21 @@ intro = [
(_("Quite easy! Isn't it?"), "intro_button", 3),
(
_("Once you finish, you can export all UI files to xml here"),
"export_all",
_("Export all"),
5,
"main-menu",
CmbTutorPosition.LEFT,
),
(
_("If you have any question, contact us on Matrix!"),
_("Contact"),
7,
None,
CmbTutorPosition.LEFT,
),
(
_("That is all for now.\nIf you find Cambalache useful please consider donating"),
"donate",
_("Donate"),
7,
"donate",
CmbTutorPosition.LEFT,

View File

@ -1,7 +1,7 @@
#
# CmbWindow
#
# Copyright (C) 2021-2023 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
@ -30,7 +30,7 @@ from gi.repository import GLib, GObject, Gio, Gdk, Gtk, Pango
from .cmb_tutor import CmbTutor, CmbTutorState
from . import cmb_tutorial
from cambalache import CmbProject, CmbUI, CmbCSS, CmbObject, CmbTypeChooserPopover, getLogger, config, _
from cambalache import CmbProject, CmbUI, CmbCSS, CmbObject, CmbTypeChooserPopover, getLogger, config, utils, _
logger = getLogger(__name__)
@ -39,15 +39,17 @@ logger = getLogger(__name__)
class CmbWindow(Gtk.ApplicationWindow):
__gtype_name__ = "CmbWindow"
__gsignals__ = {"open-project": (GObject.SignalFlags.RUN_FIRST, None, (str, str, str))}
__gsignals__ = {
"open-project": (GObject.SignalFlags.RUN_FIRST, None, (str, str, str)),
"project-saved": (GObject.SignalFlags.RUN_FIRST, None, (CmbProject, ))
}
open_filter = Gtk.Template.Child()
import_filter = Gtk.Template.Child()
open_button_box = Gtk.Template.Child()
import_button_box = Gtk.Template.Child()
headerbar = Gtk.Template.Child()
subtitle = Gtk.Template.Child()
recent_menu = Gtk.Template.Child()
undo_button = Gtk.Template.Child()
redo_button = Gtk.Template.Child()
stack = Gtk.Template.Child()
@ -85,62 +87,83 @@ class CmbWindow(Gtk.ApplicationWindow):
# Tutor widgets
intro_button = Gtk.Template.Child()
main_menu = Gtk.Template.Child()
export_all = Gtk.Template.Child()
menu_button = Gtk.Template.Child()
# Settings
completed_intro = GObject.Property(type=bool, default=False, flags=GObject.ParamFlags.READWRITE)
MAXIMIZED = 1 << 2
FULLSCREEN = 1 << 4
def __init__(self, **kwargs):
self.__project = None
self.__last_saved_index = None
self.__np_location = None
super().__init__(**kwargs)
self.editor_stack.set_size_request(420, -1)
self.actions = {}
self.open_button_box.props.homogeneous = False
self.import_button_box.props.homogeneous = False
for action in [
"open",
"create_new",
"new",
"undo",
"redo",
"intro",
"save",
"save_as",
"add_ui",
"about",
"add_css",
"copy",
"paste",
"cut",
"delete",
"add_object",
"add_object_toplevel",
"clear",
"add_placeholder",
"remove_placeholder",
"add_placeholder_row",
"remove_placeholder_row",
"import",
"export",
"add_ui",
"clear",
"close",
"debug",
"show_workspace",
"donate",
"liberapay",
"patreon",
"contact",
"about",
"copy",
"create_new",
"cut",
"debug",
"delete",
"donate",
"export",
"import",
"intro",
"liberapay",
"new",
"open",
"paste",
"patreon",
"redo",
"remove_placeholder",
"remove_placeholder_row",
"save",
"save_as",
"select_project_location",
"show_workspace",
"undo",
"workspace_restart",
"inspect"
]:
gaction = Gio.SimpleAction.new(action, None)
gaction.connect("activate", getattr(self, f"_on_{action}_activate"))
self.actions[action] = gaction
self.add_action(gaction)
# Stateful actions
for action, parameter_type, state in [
("open_recent", "s", None),
("workspace_theme", "s", "")
]:
if state is None:
gaction = Gio.SimpleAction.new(action, GLib.VariantType.new(parameter_type))
else:
gaction = Gio.SimpleAction.new_stateful(
action,
GLib.VariantType.new(parameter_type),
GLib.Variant(parameter_type, state)
)
gaction.connect("activate", getattr(self, f"_on_{action}_activate"))
self.actions[action] = gaction
self.add_action(gaction)
# Add global accelerators
action_map = [
("win.save", ["<Primary>s"]),
@ -205,7 +228,7 @@ class CmbWindow(Gtk.ApplicationWindow):
)
self.tutor = None
self.turor_waiting_for_user_action = False
self.tutor_waiting_for_user_action = False
self.__clipboard_enabled = True
self.__message_timeout_id = None
@ -237,6 +260,14 @@ class CmbWindow(Gtk.ApplicationWindow):
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
)
self.view.connect("notify::gtk-theme", self.__on_view_gtk_theme_notify)
self.connect("notify::focus-widget", self.__on_focus_widget_notify)
self.__update_recent_menu()
def __on_view_gtk_theme_notify(self, obj, pspec):
print("__on_view_gtk_theme_notify", obj.props.gtk_theme)
#self.actions["workspace_theme"].set_state(GLib.Variant("s", obj.props.gtk_theme))
@GObject.Property(type=CmbProject)
def project(self):
return self.__project
@ -246,7 +277,6 @@ class CmbWindow(Gtk.ApplicationWindow):
if self.__project is not None:
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_filename_required)
self.__project.disconnect_by_func(self.__on_project_changed)
self.__project = project
@ -267,10 +297,9 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__on_project_filename_notify(None, None)
self.__project.connect("notify::filename", self.__on_project_filename_notify)
self.__project.connect("selection-changed", self.__on_project_selection_changed)
self.__project.connect("filename-required", self.__on_project_filename_required)
self.__project.connect("changed", self.__on_project_changed)
else:
self.headerbar.set_subtitle(None)
self.subtitle.props.visible = False
self.__update_actions()
@ -279,15 +308,9 @@ class CmbWindow(Gtk.ApplicationWindow):
path = self.project.filename.replace(GLib.get_home_dir(), "~")
else:
path = _("Untitled")
self.headerbar.set_subtitle(path)
@Gtk.Template.Callback("on_about_dialog_delete_event")
def __on_about_dialog_delete_event(self, widget, event):
return True
@Gtk.Template.Callback("on_about_dialog_response")
def __on_about_dialog_response(self, widget, response_id):
widget.hide()
self.subtitle.props.visible = True
self.subtitle.props.label = path
@Gtk.Template.Callback("on_type_chooser_type_selected")
def __on_type_chooser_type_selected(self, popover, info):
@ -297,10 +320,10 @@ class CmbWindow(Gtk.ApplicationWindow):
if obj and type(obj) not in [CmbObject, CmbUI]:
return
valid, state = Gtk.get_current_event_state()
device = self.get_display().get_default_seat().get_keyboard()
# If alt is pressed, force adding object to selection
if valid and bool(state & Gdk.ModifierType.MOD1_MASK):
if device and bool(device.props.modifier_state & Gdk.ModifierType.ALT_MASK):
if obj:
parent_id = obj.object_id if isinstance(obj, CmbObject) else None
self.project.add_object(obj.ui_id, info.type_id, None, parent_id)
@ -334,12 +357,9 @@ class CmbWindow(Gtk.ApplicationWindow):
@Gtk.Template.Callback("on_view_placeholder_activated")
def __on_view_placeholder_activated(self, view, ui_id, object_id, layout, position, child_type):
r = Gdk.Rectangle()
r.x, r.y = self.view.get_pointer()
r.width = r.height = 4
obj = self.project.get_object_by_id(ui_id, object_id)
popover = CmbTypeChooserPopover(relative_to=self.view, pointing_to=r, parent_type_id=obj.type_id)
popover = CmbTypeChooserPopover(pointing_to=utils.get_pointing_to(self.view), parent_type_id=obj.type_id)
popover.set_parent(self.view)
popover.project = self.project
@ -349,13 +369,6 @@ class CmbWindow(Gtk.ApplicationWindow):
)
popover.popup()
@Gtk.Template.Callback("on_open_recent_action_item_activated")
def __on_open_recent_action_item_activated(self, recent):
uri = recent.get_current_uri()
if uri is not None:
filename, host = GLib.filename_from_uri(uri)
self.emit("open-project", filename, None, None)
@Gtk.Template.Callback("on_ui_editor_remove_ui")
def __on_ui_editor_remove_ui(self, editor):
self.__remove_object_with_confirmation(editor.object)
@ -366,9 +379,10 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__remove_object_with_confirmation(editor.object)
return True
@Gtk.Template.Callback("on_window_set_focus")
def __on_window_set_focus(self, window, widget):
types = [Gtk.Entry, Gtk.TextView, Gtk.SpinButton]
def __on_focus_widget_notify(self, obj, pspec):
widget = self.props.focus_widget
types = [Gtk.Text, Gtk.TextView]
focused_widget_needs = True
for type in types:
@ -413,16 +427,16 @@ class CmbWindow(Gtk.ApplicationWindow):
r, g, b = (linear(c.red), linear(c.green), linear(c.blue))
return 0.2126 * r + 0.7152 * g + 0.0722 * b
ctx = self.get_style_context()
# Get foreground color
fg = ctx.get_color(Gtk.StateFlags.NORMAL)
fg = self.get_color()
# If foreground luminance is closer to 1 then the background must be dark
if luminance(fg) > 0.5:
ctx.add_class("dark")
self.add_css_class("dark")
self.view._set_dark_mode(True)
else:
ctx.remove_class("dark")
self.remove_css_class("dark")
self.view._set_dark_mode(False)
def __np_name_to_ui(self, binding, value):
if len(value):
@ -549,20 +563,16 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__update_action_undo_redo()
self.__update_action_add_object()
def __file_open_dialog_new(
self, title, action=Gtk.FileChooserAction.OPEN, filter_obj=None, select_multiple=False, accept_label=None
):
dialog = Gtk.FileChooserNative(
def __file_open_dialog_new(self, title, filter_obj=None, accept_label=None):
dialog = Gtk.FileDialog(
modal=True,
title=title,
transient_for=self,
action=action,
filter=filter_obj,
select_multiple=select_multiple,
default_filter=filter_obj,
accept_label=accept_label,
)
if self.project and self.project.filename:
dialog.set_current_folder(os.path.dirname(self.project.filename))
dialog.set_initial_folder(Gio.File.new_for_path(os.path.dirname(self.project.filename)))
return dialog
@ -580,18 +590,21 @@ class CmbWindow(Gtk.ApplicationWindow):
def present_message_to_user(self, message, secondary_text=None, details=None):
dialog = Gtk.MessageDialog(
transient_for=self,
flags=0,
message_type=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.OK,
text=message,
secondary_text=secondary_text,
modal=True
)
if details:
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
box = Gtk.Box(
orientation=Gtk.Orientation.VERTICAL,
spacing=4
)
for detail in details:
box.add(
box.append(
Gtk.Label(
label=detail,
halign=Gtk.Align.START,
@ -602,12 +615,10 @@ class CmbWindow(Gtk.ApplicationWindow):
ellipsize=Pango.EllipsizeMode.END,
)
)
dialog.props.message_area.append(box)
box.show_all()
dialog.props.message_area.add(box)
dialog.run()
dialog.destroy()
dialog.connect("response", lambda d, r: dialog.destroy())
dialog.present()
def import_file(self, filename):
if self.project is None:
@ -688,25 +699,41 @@ class CmbWindow(Gtk.ApplicationWindow):
)
def _on_open_activate(self, action, data):
dialog = self.__file_open_dialog_new(_("Choose project to open"), filter_obj=self.open_filter)
if dialog.run() == Gtk.ResponseType.ACCEPT:
self.emit("open-project", dialog.get_filename(), None, None)
def dialog_callback(dialog, res):
try:
file = dialog.open_finish(res)
self.emit("open-project", file.get_path(), None, None)
except Exception as e:
pass
dialog.destroy()
dialog = self.__file_open_dialog_new(_("Choose project to open"), filter_obj=self.open_filter)
dialog.open(self, None, dialog_callback)
def _on_select_project_location_activate(self, action, data):
def dialog_callback(dialog, res):
try:
self.__np_location = dialog.select_folder_finish(res).get_path()
self.np_location_chooser.props.label = os.path.basename(self.__np_location)
except Exception as e:
pass
dialog = self.__file_open_dialog_new(_("Select project location"))
dialog.select_folder(self, None, dialog_callback)
def _on_create_new_activate(self, action, data):
self.__set_page("new_project")
self.set_focus(self.np_name_entry)
home = GLib.get_home_dir()
projects = os.path.join(home, "Projects")
directory = projects if os.path.isdir(projects) else home
if self.__np_location is None:
home = GLib.get_home_dir()
projects = os.path.join(home, "Projects")
self.__np_location = projects if os.path.isdir(projects) else home
self.np_location_chooser.set_current_folder(directory)
self.np_location_chooser.props.label = os.path.basename(self.__np_location)
def _on_new_activate(self, action, data):
name = self.np_name_entry.props.text
location = self.np_location_chooser.get_filename() or "."
uiname = self.np_ui_entry.props.text
filename = None
uipath = None
@ -718,7 +745,7 @@ class CmbWindow(Gtk.ApplicationWindow):
if len(name):
name, ext = os.path.splitext(name)
filename = os.path.join(location, name + ".cmb")
filename = os.path.join(self.__np_location, name + ".cmb")
if len(uiname) == 0:
uiname = self.np_ui_entry.props.placeholder_text
@ -728,7 +755,7 @@ class CmbWindow(Gtk.ApplicationWindow):
self.set_focus(self.np_name_entry)
return
uipath = os.path.join(location, uiname)
uipath = os.path.join(self.__np_location, uiname)
self.emit("open-project", filename, target_tk, uipath)
self.__set_page("workspace" if self.project is not None else "cambalache")
@ -743,30 +770,23 @@ class CmbWindow(Gtk.ApplicationWindow):
self.project.redo()
self.__update_action_undo_redo()
def __on_project_filename_required(self, project):
filename = None
if project.filename is None:
dialog = self.__file_open_dialog_new(_("Choose a file to save the project"), Gtk.FileChooserAction.SAVE)
if dialog.run() == Gtk.ResponseType.ACCEPT:
filename = dialog.get_filename()
dialog.destroy()
return filename
def _on_save_activate(self, action, data):
self.__save_project()
self.save_project()
def __save_dialog_callback(self, dialog, res):
try:
file = dialog.save_finish(res)
self.project.filename = file.get_path()
self.__save()
except Exception as e:
pass
def _on_save_as_activate(self, action, data):
if self.project is None:
return
dialog = self.__file_open_dialog_new(_("Choose a new file to save the project"), Gtk.FileChooserAction.SAVE)
if dialog.run() == Gtk.ResponseType.ACCEPT:
self.project.filename = dialog.get_filename()
self.__save_project()
dialog.destroy()
dialog = self.__file_open_dialog_new(_("Choose a new file to save the project"))
dialog.save(self, None, self.__save_dialog_callback)
def _on_add_ui_activate(self, action, data):
if self.project is None:
@ -785,19 +805,23 @@ class CmbWindow(Gtk.ApplicationWindow):
def __remove_object_with_confirmation(self, obj):
dialog = Gtk.MessageDialog(
transient_for=self,
flags=0,
modal=True,
message_type=Gtk.MessageType.QUESTION,
buttons=Gtk.ButtonsType.YES_NO,
text=_("Do you really want to remove {name}?").format(name=obj.get_display_name()),
)
if dialog.run() == Gtk.ResponseType.YES:
if type(obj) == CmbUI:
self.project.remove_ui(obj)
elif type(obj) == CmbCSS:
self.project.remove_css(obj)
def on_dialog_response(dialog, response):
if response == Gtk.ResponseType.YES:
if type(obj) == CmbUI:
self.project.remove_ui(obj)
elif type(obj) == CmbCSS:
self.project.remove_css(obj)
dialog.destroy()
dialog.destroy()
dialog.connect("response", on_dialog_response)
dialog.present()
def _on_copy_activate(self, action, data):
if self.project:
@ -891,30 +915,43 @@ class CmbWindow(Gtk.ApplicationWindow):
if self.project is None:
return
def dialog_callback(dialog, res):
try:
for file in dialog.open_multiple_finish(res):
self.import_file(file.get_path())
except Exception as e:
pass
dialog = self.__file_open_dialog_new(
_("Choose file to import"), filter_obj=self.import_filter, select_multiple=True, accept_label=_("Import")
_("Choose file to import"), filter_obj=self.import_filter, accept_label=_("Import")
)
dialog.open_multiple(self, None, dialog_callback)
if dialog.run() == Gtk.ResponseType.ACCEPT:
filenames = dialog.get_filenames()
dialog.destroy()
def __save(self):
if self.project.save():
self.__last_saved_index = self.project.history_index
self.__update_action_save()
self.emit("project-saved", self.project)
for filename in filenames:
self.import_file(filename)
else:
dialog.destroy()
def save_project(self):
if self.project is None:
return False
def __save_project(self):
if self.project is not None:
if self.project.save():
self.__last_saved_index = self.project.history_index
self.__update_action_save()
if self.project.filename is None:
dialog = self.__file_open_dialog_new(_("Choose a file to save the project"))
dialog.save(self, None, self.__save_dialog_callback)
return True
# Save project and update last saved index
self.__save()
return False
def _on_export_activate(self, action, data):
if self.project is None:
return
self.__save_project()
self.save_project()
n = self.project.export()
@ -931,7 +968,7 @@ class CmbWindow(Gtk.ApplicationWindow):
fd, filename = tempfile.mkstemp(".db", "cmb")
self.project.db_move_to_fs(filename)
Gtk.show_uri_on_window(self, f"file://{filename}", Gdk.CURRENT_TIME)
Gtk.show_uri(self, f"file://{filename}", Gdk.CURRENT_TIME)
def _on_about_activate(self, action, data):
self.about_dialog.present()
@ -940,13 +977,13 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__set_page("donate")
def _on_liberapay_activate(self, action, data):
Gtk.show_uri_on_window(self, "https://liberapay.com/xjuan/donate", Gdk.CURRENT_TIME)
Gtk.show_uri(self, "https://liberapay.com/xjuan/donate", Gdk.CURRENT_TIME)
def _on_patreon_activate(self, action, data):
Gtk.show_uri_on_window(self, "https://www.patreon.com/cambalache", Gdk.CURRENT_TIME)
Gtk.show_uri(self, "https://www.patreon.com/cambalache", Gdk.CURRENT_TIME)
def _on_contact_activate(self, action, data):
Gtk.show_uri_on_window(self, "https://matrix.to/#/#cambalache:gnome.org", Gdk.CURRENT_TIME)
Gtk.show_uri(self, "https://matrix.to/#/#cambalache:gnome.org", Gdk.CURRENT_TIME)
def _on_add_placeholder_activate(self, action, data):
self.view.add_placeholder()
@ -974,18 +1011,18 @@ class CmbWindow(Gtk.ApplicationWindow):
def __on_project_notify(self, obj, pspec):
if self.project:
self.turor_waiting_for_user_action = False
self.tutor_waiting_for_user_action = False
self.tutor.play()
self.disconnect_by_func(self.__on_project_notify)
def __on_object_added(self, project, obj, data):
if obj.info.is_a(data):
project.disconnect_by_func(self.__on_object_added)
self.turor_waiting_for_user_action = False
self.tutor_waiting_for_user_action = False
self.tutor.play()
def __on_ui_added(self, project, ui):
self.turor_waiting_for_user_action = False
self.tutor_waiting_for_user_action = False
project.disconnect_by_func(self.__on_ui_added)
self.tutor.play()
@ -1001,14 +1038,12 @@ class CmbWindow(Gtk.ApplicationWindow):
self.project.connect("object-added", self.__on_object_added, "GtkGrid")
elif node == "add-button":
self.project.connect("object-added", self.__on_object_added, "GtkButton")
elif node == "main-menu":
self.main_menu.props.modal = False
elif node in ["menu_button", "main-menu"]:
self.menu_button.popup()
elif node == "show-type-popover":
widget.props.popover.modal = False
widget.props.popover.popup()
elif node == "show-type-popover-gtk":
child = widget.get_children()[0]
child.props.popover.props.modal = False
child = utils.widget_get_children(widget)[0]
child.props.popover.popup()
def __on_tutor_hide_node(self, tutor, node, widget):
@ -1017,28 +1052,23 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__clear_tutor()
elif node == "add-project":
if self.__project is None:
self.turor_waiting_for_user_action = True
self.tutor_waiting_for_user_action = True
self.tutor.pause()
elif node in ["add-ui", "add-window", "add-grid", "add-button"]:
self.turor_waiting_for_user_action = True
self.tutor_waiting_for_user_action = True
self.tutor.pause()
elif node == "main-menu":
self.export_all.get_style_context().remove_class("cmb-tutor-highlight")
elif node == "donate":
self.main_menu.props.modal = True
self.main_menu.popdown()
elif node in ["menu_button", "donate"]:
self.menu_button.popdown()
elif node == "show-type-popover":
widget.props.popover.modal = True
widget.props.popover.popdown()
elif node == "show-type-popover-gtk":
child = widget.get_children()[0]
child.props.popover.props.modal = True
child = utils.widget_get_children(widget)[0]
child.props.popover.popdown()
self.__update_actions()
def _on_intro_activate(self, action, data):
if self.turor_waiting_for_user_action:
if self.tutor_waiting_for_user_action:
return
if self.tutor:
@ -1058,28 +1088,69 @@ class CmbWindow(Gtk.ApplicationWindow):
self.tutor.connect("hide-node", self.__on_tutor_hide_node)
self.tutor.play()
def _on_workspace_restart_activate(self, action, data):
self.view.restart_workspace()
def _on_workspace_theme_activate(self, action, data):
self.view.props.gtk_theme = data.get_string()
action.set_state(data)
def _on_inspect_activate(self, action, data):
self.view.inspect()
def _on_open_recent_activate(self, action, data):
self.emit("open-project", data.get_string(), None, None)
def __update_recent_menu(self):
manager = Gtk.RecentManager.get_default()
mime_types = [
"application/x-cambalache-project"
]
for recent in manager.get_items():
if recent.get_mime_type() not in mime_types:
continue
filename, host = GLib.filename_from_uri(recent.get_uri())
if not os.path.exists(filename):
continue
item = Gio.MenuItem()
item.set_label(recent.get_display_name())
item.set_action_and_target_value("win.open_recent", GLib.Variant("s", filename))
self.recent_menu.append_item(item)
def __load_window_state(self):
state = self.window_settings.get_uint("state")
if state & Gdk.WindowState.MAXIMIZED:
if state & self.MAXIMIZED:
self.maximize()
elif state & self.FULLSCREEN:
self.fullscreen()
else:
size = self.window_settings.get_value("size").unpack()
self.set_default_size(*size)
def __save_window_state(self):
state = self.props.window.get_state()
fullscreen = self.props.fullscreened
maximized = self.props.maximized
state = 0
fullscreen = state & Gdk.WindowState.FULLSCREEN
maximized = state & Gdk.WindowState.MAXIMIZED
if fullscreen:
state = state | self.FULLSCREEN
if maximized:
state = state | self.MAXIMIZED
# Maintain compatibility with Gtk 3 state
self.window_settings.set_uint("state", state)
size = (0, 0) if fullscreen or maximized else self.get_size()
size = (0, 0) if fullscreen or maximized else (self.props.default_width, self.props.default_height)
self.window_settings.set_value("size", GLib.Variant("(ii)", size))
def do_delete_event(self, event):
def do_close_request(self):
self.__save_window_state()
return False

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
#
# CmbContextMenu - Cambalache UI Editor
#
# Copyright (C) 2021 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
@ -23,7 +23,7 @@
import os
from gi.repository import GObject, GLib, Gdk, Gtk
from gi.repository import GObject, GLib, Gio, Gdk, Gtk
from cambalache import _
@ -31,23 +31,17 @@ from cambalache import _
class CmbContextMenu(Gtk.PopoverMenu):
__gtype_name__ = "CmbContextMenu"
gtk_theme = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
target_tk = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
main_box = Gtk.Template.Child()
separator = Gtk.Template.Child()
css_theme = Gtk.Template.Child()
css_theme_box = Gtk.Template.Child()
main_section = Gtk.Template.Child()
def __init__(self, **kwargs):
self.theme_submenu = None
super().__init__(**kwargs)
self.connect("notify::target-tk", lambda o, p: self.__populate_css_theme_box())
def __on_css_theme_button_toggled(self, button, data):
if button.props.active:
self.gtk_theme = data
def __populate_css_theme_box(self):
gtk_path = "gtk-3.0"
@ -56,9 +50,17 @@ class CmbContextMenu(Gtk.PopoverMenu):
if self.target_tk == "gtk-4.0":
gtk_path = "gtk-4.0"
# FIXME: whats the real default theme for gtk4?
themes = ["Default"]
else:
themes = ["Adwaita", "HighContrast", "HighContrastInverse"]
for child in self.css_theme_box.get_children():
self.css_theme_box.remove(child)
if self.theme_submenu is None:
self.theme_submenu = Gio.Menu()
self.main_section.prepend_submenu(_("CSS theme"), self.theme_submenu)
# Remove all items from theme submenu
self.theme_submenu.remove_all()
dirs = []
@ -71,9 +73,6 @@ class CmbContextMenu(Gtk.PopoverMenu):
# Append ~/.themes
dirs.append(os.path.join(GLib.get_home_dir(), ".themes"))
# Default themes
themes = ["Adwaita", "HighContrast", "HighContrastInverse"]
for path in dirs:
if not os.path.isdir(path):
continue
@ -86,23 +85,16 @@ class CmbContextMenu(Gtk.PopoverMenu):
# Dedup and sort
themes = list(dict.fromkeys(themes))
# Add back item
button = Gtk.ModelButton(text=_("CSS themes"), menu_name="main", inverted=True, centered=True, visible=True)
self.css_theme_box.add(button)
group = None
for theme in sorted(themes):
button = Gtk.RadioButton(label=theme, group=group, active=self.gtk_theme == theme, visible=True)
if group is None:
group = button
button.connect("toggled", self.__on_css_theme_button_toggled, theme)
self.css_theme_box.add(button)
self.separator.props.visible = self.css_theme.props.visible = len(themes) > 0
item = Gio.MenuItem()
item.set_label(theme)
item.set_action_and_target_value("win.workspace_theme", GLib.Variant("s", theme))
self.theme_submenu.append_item(item)
def popup_at(self, x, y):
r = Gdk.Rectangle()
r.x, r.y, r.width, r.height = (x, y, 10, 10)
r.x, r.y = (x, y)
r.width = r.height = 0
self.set_pointing_to(r)
self.popup()

View File

@ -1,182 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="gtk" version="4.0"/>
<menu id="menu_model">
<section>
<item>
<attribute name="action">win.cut</attribute>
<attribute name="label" translatable="1">Cut</attribute>
</item>
<item>
<attribute name="action">win.copy</attribute>
<attribute name="label" translatable="1">Copy</attribute>
</item>
<item>
<attribute name="action">win.paste</attribute>
<attribute name="label" translatable="1">Paste</attribute>
</item>
<item>
<attribute name="action">win.delete</attribute>
<attribute name="label" translatable="1">Delete</attribute>
</item>
</section>
<section>
<item>
<attribute name="action">win.add_object</attribute>
<attribute name="label" translatable="1">Add object here</attribute>
</item>
<item>
<attribute name="action">win.add_object_toplevel</attribute>
<attribute name="label" translatable="1">Add object as toplevel</attribute>
</item>
<item>
<attribute name="action">win.clear</attribute>
<attribute name="label" translatable="1">Clear Properties</attribute>
</item>
<item>
<attribute name="action">win.documentation</attribute>
<attribute name="label" translatable="1">Read Documentation</attribute>
</item>
</section>
<section id="main_section"/>
</menu>
<template class="CmbContextMenu" parent="GtkPopoverMenu">
<property name="can-focus">False</property>
<child>
<object class="GtkBox" id="main_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">4</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.cut</property>
<property name="text" translatable="yes">Cut</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.copy</property>
<property name="text" translatable="yes">Copy</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.paste</property>
<property name="text" translatable="yes">Paste</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.delete</property>
<property name="text" translatable="yes">Delete</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.add_object</property>
<property name="text" translatable="yes">Add object here</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.add_object_toplevel</property>
<property name="text" translatable="yes">Add object as toplevel</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.clear</property>
<property name="text" translatable="yes">Clear Properties</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">7</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="action-name">win.documentation</property>
<property name="text" translatable="yes">Read Documentation</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">8</property>
</packing>
</child>
<child>
<object class="GtkSeparator" id="separator">
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">9</property>
</packing>
</child>
<child>
<object class="GtkModelButton" id="css_theme">
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="text" translatable="yes">CSS theme</property>
<property name="menu-name">css-theme</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">10</property>
</packing>
</child>
</object>
<packing>
<property name="submenu">main</property>
</packing>
</child>
<child>
<object class="GtkBox" id="css_theme_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">4</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="submenu">css-theme</property>
<property name="position">1</property>
</packing>
</child>
<property name="menu-model">menu_model</property>
</template>
</interface>

View File

@ -23,6 +23,7 @@
from gi.repository import GObject, Gtk
from .cmb_css import CmbCSS
from . import utils
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_css_editor.ui")
@ -121,8 +122,7 @@ class CmbCSSEditor(Gtk.Grid):
def __update_provider_for(self):
# Remove all css_ui check buttons
ui_box_children = self.ui_box.get_children()
for child in ui_box_children:
for child in utils.widget_get_children(self.ui_box):
self.ui_box.remove(child)
if self._object is None:
@ -137,7 +137,7 @@ class CmbCSSEditor(Gtk.Grid):
label=ui.get_display_name(), active=ui.ui_id in provider_for, halign=Gtk.Align.START, visible=True
)
check.connect("toggled", self.__on_check_button_toggled, ui)
self.ui_box.add(check)
self.ui_box.append(check)
def __on_file_changed(self, obj):
self.infobar.set_revealed(True)

View File

@ -1,89 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="gladecambalache" version="0.0"/>
<!-- n-columns=2 n-rows=6 -->
<requires lib="gtk" version="4.0"/>
<template class="CmbCSSEditor" parent="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">4</property>
<property name="column-spacing">3</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Filename:</property>
<property name="label" translatable="1">Filename:</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Priority:</property>
<property name="label" translatable="1">Priority:</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">This provider will be used in all UI.</property>
<property name="tooltip-text" translatable="1">This provider will be used in all UI.</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Global:</property>
<property name="label" translatable="1">Global:</property>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="CmbEntry" id="filename">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="placeholder-text" translatable="yes">&lt;file name relative to project&gt;</property>
<property name="placeholder-text" translatable="1">&lt;file name relative to project&gt;</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="priority">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focusable">1</property>
<property name="halign">start</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="is_global">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focusable">1</property>
<property name="halign">start</property>
<layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="ui_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
@ -94,189 +79,116 @@
<child>
<placeholder/>
</child>
<layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">List of UI where this provider will be used</property>
<property name="tooltip-text" translatable="1">List of UI where this provider will be used</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="label" translatable="yes">Provider for:</property>
<property name="label" translatable="1">Provider for:</property>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">4</property>
<child>
<object class="GtkButton" id="remove_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Remove CSS file from project</property>
<property name="focusable">1</property>
<!-- <property name="receives-default">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" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">app-remove-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label" translatable="yes">&lt;small&gt;Note: CSS files need to be loaded at runtime&lt;/small&gt;</property>
<property name="use-markup">True</property>
<property name="label" translatable="1">&lt;small&gt;Note: CSS files need to be loaded at runtime&lt;/small&gt;</property>
<property name="use-markup">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Save CSS file</property>
<property name="sensitive">0</property>
<property name="focusable">1</property>
<property name="receives-default">1</property>
<property name="tooltip-text" translatable="1">Save CSS file</property>
<property name="halign">start</property>
<property name="valign">end</property>
<signal name="clicked" handler="on_save_button_clicked" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">document-save-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
<layout>
<property name="column">0</property>
<property name="row">5</property>
<property name="column-span">2</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">5</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label-xalign">0</property>
<property name="shadow-type">in</property>
<child>
<property name="child">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkInfoBar" id="infobar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="message-type">warning</property>
<property name="show-close-button">True</property>
<property name="revealed">False</property>
<property name="show-close-button">1</property>
<property name="revealed">0</property>
<signal name="response" handler="on_infobar_response" swapped="no"/>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can-focus">False</property>
<property name="spacing">6</property>
<property name="layout-style">end</property>
<child>
<child type="action">
<object class="GtkButton" id="reload_button">
<property name="label" translatable="yes">Reload</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="label" translatable="1">Reload</property>
<property name="focusable">1</property>
<property name="receives-default">1</property>
<property name="halign">start</property>
<property name="valign">end</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child internal-child="content_area">
<child>
<object class="GtkBox">
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="visible">0</property>
<property name="hexpand">1</property>
<property name="spacing">16</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">The file changed on disk.</property>
<property name="hexpand">1</property>
<property name="label" translatable="1">The file changed on disk.</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<action-widgets>
<action-widget response="-5">reload_button</action-widget>
</action-widgets>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="vexpand">True</property>
<property name="focusable">1</property>
<property name="vexpand">1</property>
<child>
<object class="CmbSourceView" id="view">
<property name="visible">True</property>
@ -285,23 +197,18 @@
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</property>
<child type="label_item">
<placeholder/>
</child>
<layout>
<property name="column">0</property>
<property name="row">4</property>
<property name="column-span">2</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
<property name="width">2</property>
</packing>
</child>
</template>
</interface>

View File

@ -1,7 +1,7 @@
#
# CmbDB - Cambalache DataBase
#
# Copyright (C) 2021-2023 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
@ -28,7 +28,7 @@ import ast
from lxml import etree
from lxml.builder import E
from gi.repository import Gio, GObject, Gtk
from gi.repository import GLib, Gio, GObject
from cambalache import config, getLogger, _
from . import cmb_db_migration, utils
from .constants import EXTERNAL_TYPE, GMENU_TYPE, GMENU_SECTION_TYPE, GMENU_SUBMENU_TYPE, GMENU_ITEM_TYPE
@ -618,8 +618,8 @@ class CmbDB(GObject.GObject):
return f"\t({r})"
def _dump_table(c, table):
c.execute(f"SELECT * FROM {table};")
def _dump_query(c, query):
c.execute(query)
row = c.fetchone()
if row is None:
@ -635,20 +635,27 @@ class CmbDB(GObject.GObject):
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_table(c, table)
data = _dump_query(c, f"SELECT * FROM {table};")
append_data(project, table, data)
if data is None:
continue
element = etree.Element(table)
element.text = data
project.append(element)
# 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:
@ -1523,12 +1530,11 @@ class CmbDB(GObject.GObject):
if column is not None:
c.execute(f"UPDATE ui SET {column}=? WHERE ui_id=?", (value, ui_id))
else:
print(child)
custom_fragments.append(child)
# self.__unknown_tag(child, None, child.tag)
while Gtk.events_pending():
Gtk.main_iteration_do(False)
main_loop = GLib.MainContext.default()
while main_loop.pending():
main_loop.iteration(False)
# Fix object references!
self.__fix_object_references(ui_id)

View File

@ -1,31 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="gladecambalache" version="0.0"/>
<requires lib="gtk" version="4.0"/>
<template class="CmbFragmentEditor" parent="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Extra fragments:</property>
<property name="label" translatable="1">Extra fragments:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<property name="vexpand">1</property>
<property name="focusable">1</property>
<child>
<object class="CmbSourceView" id="view">
<property name="visible">True</property>
@ -34,11 +22,6 @@
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</template>
</interface>

View File

@ -71,7 +71,8 @@ class CmbObjectDataEditor(Gtk.Box):
else:
self.__data.parent.remove_data(self.__data)
@Gtk.Template.Callback("on_remove_size_allocate")
# FIXME: GTK4
#@Gtk.Template.Callback("on_remove_size_allocate")
def __on_remove_size_allocate(self, button, alloc):
info = self.data.info if self.data else self.info
@ -182,9 +183,9 @@ class CmbObjectDataEditor(Gtk.Box):
child_info = info.children[child]
button = Gtk.ModelButton(label=_("Add {key}").format(key=child_info.key), visible=True)
button.connect("clicked", self.__on_child_button_clicked, child_info)
box.add(button)
box.append(button)
popover.add(box)
popover.set_child(box)
return popover
@ -236,7 +237,7 @@ class CmbObjectDataEditor(Gtk.Box):
nchildren = len(info.children)
self.remove_button.set_tooltip_text(_("Remove {key}").format(key=info.key))
self.remove_button.props.tooltip_text = _("Remove {key}").format(key=info.key)
# Add a menu if there is more than one child type
if nchildren > 1:
@ -244,7 +245,7 @@ class CmbObjectDataEditor(Gtk.Box):
self.add_child.set_visible(True)
elif nchildren:
key = list(info.children.keys())[0]
self.add_only_child.set_tooltip_text(_("Add {key}").format(key=key))
self.add_only_child.props.tooltip_text = _("Add {key}").format(key=key)
self.add_only_child.set_visible(True)
# Item name
@ -267,7 +268,7 @@ class CmbObjectDataEditor(Gtk.Box):
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
)
self.top_box.add(editor)
self.top_box.append(editor)
nargs = len(info.args)
@ -287,7 +288,7 @@ class CmbObjectDataEditor(Gtk.Box):
# Special case items with one argument and no value (like styles)
if nargs == 1 and not info.type_id:
self.label.props.label = f"{info.key} {arg_info.key}"
self.top_box.add(editor)
self.top_box.append(editor)
else:
label = Gtk.Label(visible=True, label=arg_info.key, xalign=1)
self.__add(editor, label)

View File

@ -1,74 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<!-- Created with Cambalache 0.11.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="gtk" version="4.0"/>
<template class="CmbObjectDataEditor" parent="GtkBox">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child>
<object class="GtkBox" id="top_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="no-show-all">True</property>
<property name="spacing">4</property>
<child>
<object class="GtkMenuButton" id="add_child">
<property name="can-focus">False</property>
<property name="receives-default">False</property>
<property name="visible">0</property>
<property name="halign">end</property>
<property name="relief">none</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">list-add-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="add_only_child">
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="visible">0</property>
<property name="focusable">1</property>
<property name="halign">end</property>
<property name="relief">none</property>
<signal name="clicked" handler="on_add_only_child_clicked" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">list-add-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="remove_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="focusable">1</property>
<property name="halign">end</property>
<property name="relief">none</property>
<signal name="clicked" handler="on_remove_clicked" swapped="no"/>
<signal name="size-allocate" handler="on_remove_size_allocate" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">user-trash-symbolic</property>
</object>
</child>
@ -76,36 +45,15 @@
<class name="hidden"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
<object class="GtkLabel" id="label"/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=1 -->
<object class="GtkGrid" id="grid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="vexpand">1</property>
<property name="row-spacing">4</property>
<property name="column-spacing">4</property>
<child>
@ -115,11 +63,6 @@
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</template>
</interface>

View File

@ -29,9 +29,10 @@ from .control import CmbEntry, CmbChildTypeComboBox, cmb_create_editor
from .cmb_property_label import CmbPropertyLabel
from cambalache import _
from .constants import EXTERNAL_TYPE
from . import utils
class CmbObjectEditor(Gtk.ScrolledWindow):
class CmbObjectEditor(Gtk.Box):
__gtype_name__ = "CmbObjectEditor"
layout = GObject.Property(type=bool, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, default=False)
@ -43,10 +44,7 @@ class CmbObjectEditor(Gtk.ScrolledWindow):
super().__init__(**kwargs)
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, visible=True)
viewport = Gtk.Viewport(visible=True, shadow_type=Gtk.ShadowType.NONE)
viewport.add(self.box)
self.add(viewport)
self.props.orientation = Gtk.Orientation.VERTICAL
def __create_id_editor(self):
grid = Gtk.Grid(hexpand=True, row_spacing=4, column_spacing=4)
@ -97,12 +95,12 @@ class CmbObjectEditor(Gtk.ScrolledWindow):
box = Gtk.FlowBox(visible=True, hexpand=True, selection_mode=Gtk.SelectionMode.NONE)
label = Gtk.Label(label=_("Add"), xalign=0, visible=True)
box.add(label)
box.append(label)
for type_id in info.child_type_shortcuts:
button = Gtk.Button(label=type_id, visible=True)
button.connect("clicked", self.__on_shortcut_button_clicked, type_id)
box.add(button)
box.append(button)
return box
@ -127,7 +125,7 @@ class CmbObjectEditor(Gtk.ScrolledWindow):
def __create_child_type_editor(self):
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
box.add(Gtk.Label(label=_("Child Type"), width_chars=8))
box.append(Gtk.Label(label=_("Child Type"), width_chars=8))
combo = CmbChildTypeComboBox(object=self.__object)
@ -138,12 +136,12 @@ class CmbObjectEditor(Gtk.ScrolledWindow):
"cmb-value",
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
)
box.pack_start(combo, True, True, 0)
box.append(combo)
return box
def __update_view(self):
for child in self.box.get_children():
self.box.remove(child)
for child in utils.widget_get_children(self):
self.remove(child)
if self.__object is None:
return
@ -158,10 +156,10 @@ class CmbObjectEditor(Gtk.ScrolledWindow):
# Child Type input
if parent.info.has_child_types():
self.box.add(self.__create_child_type_editor())
self.append(self.__create_child_type_editor())
else:
# ID
self.box.add(self.__create_id_editor())
self.append(self.__create_id_editor())
if obj.type_id == EXTERNAL_TYPE:
label = Gtk.Label(
@ -174,8 +172,8 @@ It has to be exposed by your application with GtkBuilder expose_object method."
xalign=0,
wrap=True,
)
self.box.add(label)
self.show_all()
self.append(label)
self.show()
return
info = parent.info if self.layout and parent else obj.info
@ -261,11 +259,11 @@ It has to be exposed by your application with GtkBuilder expose_object method."
expander = Gtk.Expander(label=f"<b>{owner_id}</b>", use_markup=True, expanded=True)
revealer = Gtk.Revealer(reveal_child=True)
expander.connect("notify::expanded", self.__on_expander_expanded, revealer)
revealer.add(grid)
self.box.add(expander)
self.box.add(revealer)
revealer.set_child(grid)
self.append(expander)
self.append(revealer)
self.show_all()
self.show()
def __on_object_ui_notify(self, obj, pspec):
if pspec.name == "template-id" and self.__template_switch:

View File

@ -78,7 +78,6 @@ class CmbProject(Gtk.TreeStore):
"type-info-added": (GObject.SignalFlags.RUN_FIRST, None, (CmbTypeInfo,)),
"type-info-removed": (GObject.SignalFlags.RUN_FIRST, None, (CmbTypeInfo,)),
"type-info-changed": (GObject.SignalFlags.RUN_FIRST, None, (CmbTypeInfo,)),
"filename-required": (GObject.SignalFlags.RUN_FIRST, str, ()),
}
target_tk = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT)
@ -282,9 +281,6 @@ class CmbProject(Gtk.TreeStore):
self.__filename = value
def save(self):
if self.filename is None:
self.filename = self.emit("filename-required")
if self.filename:
self.db.save(self.filename)
return True
@ -378,6 +374,9 @@ class CmbProject(Gtk.TreeStore):
return (ui, msgs, detail_msg)
def __export(self, ui_id, filename, dirname=None):
if filename is None:
return
if not os.path.isabs(filename):
if dirname is None:
dirname = os.path.dirname(self.filename)
@ -1178,14 +1177,15 @@ class CmbProject(Gtk.TreeStore):
self.__object_update_row(ui.ui_id, template_id)
def _ui_changed(self, ui, field):
iter = self.get_iter_from_object(ui)
if field == "template-id":
self.__update_template_type_info(ui)
iter = self.get_iter_from_object(ui)
if iter is None:
return
path = self.get_path(iter)
self.row_changed(path, iter)
self.emit("ui-changed", ui, field)
def _ui_library_changed(self, ui, lib):

View File

@ -43,8 +43,6 @@ class CmbPropertyLabel(Gtk.Button):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.props.relief = Gtk.ReliefStyle.NONE
if not self.prop and not self.layout_prop:
raise Exception("CmbPropertyLabel requires prop or layout_prop to be set")
return
@ -54,8 +52,8 @@ class CmbPropertyLabel(Gtk.Button):
# Update label status
if self.prop:
self.bind_icon = Gtk.Image(icon_size=Gtk.IconSize.MENU, visible=True)
box.add(self.bind_icon)
self.bind_icon = Gtk.Image(icon_size=Gtk.IconSize.NORMAL, visible=True)
box.append(self.bind_icon)
self.label.props.label = self.prop.property_id
@ -71,24 +69,22 @@ class CmbPropertyLabel(Gtk.Button):
self.__update_layout_property_label()
self.layout_prop.connect("notify::value", lambda o, p: self.__update_layout_property_label())
box.add(self.label)
self.add(box)
box.append(self.label)
self.set_child(box)
def __update_label(self, prop):
style = self.get_style_context()
if prop.value != prop.info.default_value:
style.add_class("modified")
self.add_css_class("modified")
else:
style.remove_class("modified")
self.remove_css_class("modified")
msg = prop.version_warning
self.set_tooltip_text(msg)
if msg:
style.add_class("warning")
self.add_css_class("warning")
else:
style.remove_class("warning")
self.remove_css_class("warning")
def __update_layout_property_label(self):
self.__update_label(self.layout_prop)
@ -101,10 +97,10 @@ class CmbPropertyLabel(Gtk.Button):
if self.prop.bind_property_id:
self.bind_icon.props.icon_name = "binded-symbolic"
self.get_style_context().remove_class("hidden")
self.remove_css_class("hidden")
else:
self.bind_icon.props.icon_name = "bind-symbolic"
self.get_style_context().add_class("hidden")
self.add_css_class("hidden")
def __on_object_editor_notify(self, object_editor, pspec, property_editor):
object_id = object_editor.cmb_value
@ -129,9 +125,10 @@ class CmbPropertyLabel(Gtk.Button):
popover.popdown()
def __on_bind_button_clicked(self, button):
popover = Gtk.Popover(relative_to=button, position=Gtk.PositionType.LEFT)
popover = Gtk.Popover(position=Gtk.PositionType.LEFT)
popover.set_parent(self)
grid = Gtk.Grid(hexpand=True, row_spacing=4, column_spacing=4, border_width=4, visible=True)
grid = Gtk.Grid(hexpand=True, row_spacing=4, column_spacing=4, visible=True)
grid.attach(Gtk.Label(label="<b>Property Binding</b>", use_markup=True, visible=True, xalign=0), 0, 0, 2, 1)
@ -177,7 +174,7 @@ class CmbPropertyLabel(Gtk.Button):
grid.attach(clear, 0, i, 2, 1)
object_editor.grab_focus()
popover.add(grid)
popover.set_child(grid)
popover.popup()

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="gtk" version="4.0"/>
<object class="GtkEntryCompletion" id="handler_entrycompletion">
<child>
<object class="GtkCellRendererText"/>
@ -12,42 +11,28 @@
</object>
<object class="GtkTreeStore" id="treestore">
<columns>
<!-- column-name signal -->
<column type="GObject"/>
<!-- column-name owner_id -->
<column type="gchararray"/>
<!-- column-name signal_id -->
<column type="gchararray"/>
<!-- column-name detail -->
<column type="gchararray"/>
<!-- column-name handler -->
<column type="gchararray"/>
<!-- column-name user_data -->
<column type="gchararray"/>
<!-- column-name swap -->
<column type="gboolean"/>
<!-- column-name after -->
<column type="gboolean"/>
<!-- column-name info -->
<column type="GObject"/>
<!-- column-name version_warning -->
<column type="gchararray"/>
</columns>
</object>
<template class="CmbSignalEditor" parent="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<property name="vexpand">1</property>
<property name="focusable">1</property>
<property name="child">
<object class="GtkTreeView" id="treeview">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focusable">1</property>
<property name="model">treestore</property>
<property name="tooltip-column">9</property>
<child internal-child="selection">
@ -55,9 +40,9 @@
</child>
<child>
<object class="GtkTreeViewColumn" id="signal_id_column">
<property name="resizable">True</property>
<property name="resizable">1</property>
<property name="min-width">64</property>
<property name="title" translatable="yes">Signal</property>
<property name="title" translatable="1">Signal</property>
<child>
<object class="GtkCellRendererText" id="signal_id">
<signal name="edited" handler="on_detail_edited" swapped="no"/>
@ -70,13 +55,13 @@
</child>
<child>
<object class="GtkTreeViewColumn" id="handler_column">
<property name="resizable">True</property>
<property name="resizable">1</property>
<property name="min-width">64</property>
<property name="title" translatable="yes">Handler</property>
<property name="expand">True</property>
<property name="title" translatable="1">Handler</property>
<property name="expand">1</property>
<child>
<object class="GtkCellRendererText" id="handler">
<property name="editable">True</property>
<property name="editable">1</property>
<property name="placeholder-text">&lt;Enter callback&gt;</property>
<signal name="edited" handler="on_handler_edited" swapped="no"/>
</object>
@ -88,10 +73,10 @@
</child>
<child>
<object class="GtkTreeViewColumn" id="user_data_column">
<property name="title" translatable="yes">Data</property>
<property name="title" translatable="1">Data</property>
<child>
<object class="GtkCellRendererText" id="user_data">
<property name="editable">True</property>
<property name="editable">1</property>
<property name="placeholder-text">&lt;object&gt;</property>
<signal name="edited" handler="on_user_data_edited" swapped="no"/>
</object>
@ -103,7 +88,7 @@
</child>
<child>
<object class="GtkTreeViewColumn" id="swap_column">
<property name="title" translatable="yes">Swap</property>
<property name="title" translatable="1">Swap</property>
<child>
<object class="GtkCellRendererToggle" id="swap">
<signal name="toggled" handler="on_swap_toggled" swapped="no"/>
@ -116,7 +101,7 @@
</child>
<child>
<object class="GtkTreeViewColumn" id="after_column">
<property name="title" translatable="yes">After</property>
<property name="title" translatable="1">After</property>
<child>
<object class="GtkCellRendererToggle" id="after">
<signal name="toggled" handler="on_after_toggled" swapped="no"/>
@ -128,13 +113,8 @@
</object>
</child>
</object>
</child>
</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</template>
</interface>

View File

@ -40,7 +40,6 @@ class CmbTreeView(Gtk.TreeView):
self.__in_selection_change = False
self._selection.connect("changed", self.__on_selection_changed)
self.set_headers_visible(False)
self.__right_click = False
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Object(Type)", renderer)
@ -50,31 +49,14 @@ class CmbTreeView(Gtk.TreeView):
self.connect("notify::model", self.__on_model_notify)
self.connect("row-activated", self.__on_row_activated)
self.menu = CmbContextMenu(relative_to=self)
self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK)
self.connect("button-press-event", self.__on_button_press_event)
self.connect("button-release-event", self.__on_button_release_event)
gesture = Gtk.GestureClick(button=3)
gesture.connect("pressed", self.__on_button_press)
self.add_controller(gesture)
self.set_reorderable(True)
def __on_button_press_event(self, widget, event):
if event.window != self.get_bin_window() or event.button != 3:
return False
self.__right_click = True
return True
def __on_button_release_event(self, widget, event):
if event.window != self.get_bin_window() or event.button != 3:
return False
if not self.__right_click:
return False
self.__right_click = False
retval = self.get_path_at_pos(event.x, event.y)
def __on_button_press(self, widget, npress, x, y):
retval = self.get_path_at_pos(x, y)
if retval is None:
return False
@ -82,7 +64,12 @@ class CmbTreeView(Gtk.TreeView):
path, col, xx, yy = retval
self.get_selection().select_path(path)
self.menu.popup_at(event.x, event.y)
menu = CmbContextMenu()
# Use parent instead of self to avoid warning and focus not working properly
# (run-dev.py:188589): Gtk-CRITICAL **: 16:45:12.790: gtk_css_node_insert_after: assertion 'previous_sibling == NULL || previous_sibling->parent == parent' failed
menu.set_parent(self.props.parent)
menu.popup_at(x, y)
return True
@ -156,7 +143,7 @@ class CmbTreeView(Gtk.TreeView):
project.set_selection([obj])
def do_query_tooltip(self, x, y, keyboard_mode, tooltip):
retval, xx, yy, model, path, iter_ = self.get_tooltip_context(x, y, keyboard_mode)
retval, model, path, iter_ = self.get_tooltip_context(x, y, keyboard_mode)
if not retval:
return False

View File

@ -1,216 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="gladecambalache" version="0.0"/>
<requires lib="gtk" version="4.0"/>
<object class="CmbTypeChooserPopover" id="all">
<property name="can-focus">False</property>
<property name="show-categories">True</property>
</object>
<object class="CmbTypeChooserPopover" id="control">
<property name="can-focus">False</property>
<property name="category">control</property>
</object>
<object class="CmbTypeChooserPopover" id="display">
<property name="can-focus">False</property>
<property name="category">display</property>
</object>
<object class="CmbTypeChooserPopover" id="extra">
<property name="can-focus">False</property>
<property name="uncategorized-only">True</property>
</object>
<object class="CmbTypeChooserPopover" id="layout">
<property name="can-focus">False</property>
<property name="category">layout</property>
</object>
<object class="CmbTypeChooserPopover" id="model">
<property name="can-focus">False</property>
<property name="category">model</property>
</object>
<object class="CmbTypeChooserPopover" id="toplevel">
<property name="can-focus">False</property>
<property name="category">toplevel</property>
</object>
<template class="CmbTypeChooser" parent="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">4</property>
<child>
<object class="GtkMenuButton" id="type_chooser_all">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="focusable">1</property>
<property name="focus-on-click">0</property>
<property name="receives-default">1</property>
<property name="popover">all</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">edit-find-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox" id="type_chooser_gtk">
<object class="GtkBox" id="type_chooser_gtk">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="layout-style">expand</property>
<property name="homogeneous">False</property>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="focusable">1</property>
<property name="focus-on-click">0</property>
<property name="receives-default">1</property>
<property name="popover">toplevel</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes" comments="Widget group for toplevels/windows">Toplevel</property>
<property name="label" translatable="1" comments="Widget group for toplevels/windows">Toplevel</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="focusable">1</property>
<property name="focus-on-click">0</property>
<property name="receives-default">1</property>
<property name="popover">layout</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes" comments="Widget group for container widgets liek GtkBox grid">Layout</property>
<property name="label" translatable="1" comments="Widget group for container widgets liek GtkBox grid">Layout</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="focusable">1</property>
<property name="focus-on-click">0</property>
<property name="receives-default">1</property>
<property name="popover">control</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes" comments="Widget group for control wildget like buttons, entries">Control</property>
<property name="label" translatable="1" comments="Widget group for control wildget like buttons, entries">Control</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="focusable">1</property>
<property name="focus-on-click">0</property>
<property name="receives-default">1</property>
<property name="popover">display</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes" comments="Widget group for display widgets (label, image)">Display</property>
<property name="label" translatable="1" comments="Widget group for display widgets (label, image)">Display</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="focusable">1</property>
<property name="focus-on-click">0</property>
<property name="receives-default">1</property>
<property name="popover">model</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes" comments="Widget group for model objects (ListStore, TextBuffer)">Model</property>
<property name="label" translatable="1" comments="Widget group for model objects (ListStore, TextBuffer)">Model</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="focusable">1</property>
<property name="receives-default">1</property>
<property name="popover">extra</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">pan-down-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="type_label">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">False</property>
<property name="hexpand">1</property>
<property name="sensitive">0</property>
<property name="halign">start</property>
<attributes>
<attribute name="style" value="italic"/>
<attribute name="style" value="italic"></attribute>
</attributes>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
</template>
</interface>

View File

@ -47,8 +47,9 @@ class CmbTypeChooserPopover(Gtk.Popover):
self._chooser = CmbTypeChooserWidget()
self._chooser.connect("type-selected", self.__on_type_selected)
self._chooser.show_all()
self.add(self._chooser)
self.set_child(self._chooser)
self.set_default_widget(self._chooser)
for prop in [
"project",

View File

@ -46,7 +46,6 @@ class CmbTypeChooserWidget(Gtk.Box):
parent_type_id = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
derived_type_id = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
entrycompletion = Gtk.Template.Child()
scrolledwindow = Gtk.Template.Child()
treeview = Gtk.Template.Child()
@ -58,7 +57,7 @@ class CmbTypeChooserWidget(Gtk.Box):
super().__init__(**kwargs)
self.connect("map", self.__on_map)
#self.connect("map", self.__on_map)
def __type_info_should_append(self, info):
retval = False
@ -152,7 +151,6 @@ class CmbTypeChooserWidget(Gtk.Box):
if self._filter:
self._filter.set_visible_func(self.__visible_func)
self.entrycompletion.props.model = self.__model
self.treeview.props.model = self._filter
if project is not None:
@ -191,10 +189,10 @@ class CmbTypeChooserWidget(Gtk.Box):
return type_id_lower.find(self._search_text) >= 0
def __on_map(self, widget):
toplevel = widget.get_toplevel()
root = widget.get_root()
if toplevel:
height = toplevel.get_allocated_height() - 100
if root:
height = root.get_allocated_height() - 100
if height > 460:
height = height * 0.7

View File

@ -1,54 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkEntryCompletion" id="entrycompletion">
<property name="text-column">0</property>
<property name="inline-completion">True</property>
<property name="popup-completion">False</property>
<property name="popup-single-match">False</property>
</object>
<requires lib="gtk" version="4.0"/>
<template class="CmbTypeChooserWidget" parent="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkSearchEntry" id="searchentry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="primary-icon-name">edit-find-symbolic</property>
<property name="primary-icon-activatable">False</property>
<property name="primary-icon-sensitive">False</property>
<property name="completion">entrycompletion</property>
<property name="input-hints">GTK_INPUT_HINT_WORD_COMPLETION | GTK_INPUT_HINT_NONE</property>
<signal name="activate" handler="on_searchentry_activate" swapped="no"/>
<signal name="search-changed" handler="on_searchentry_search_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hscrollbar-policy">never</property>
<property name="window-placement">bottom-left</property>
<property name="shadow-type">in</property>
<property name="max-content-height">512</property>
<property name="propagate-natural-width">True</property>
<property name="propagate-natural-height">True</property>
<child>
<property name="propagate-natural-width">1</property>
<property name="propagate-natural-height">1</property>
<property name="child">
<object class="GtkTreeView" id="treeview">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="headers-visible">False</property>
<property name="enable-search">False</property>
<property name="activate-on-single-click">True</property>
<property name="headers-visible">0</property>
<property name="enable-search">0</property>
<property name="activate-on-single-click">1</property>
<signal name="row-activated" handler="on_treeview_row_activated" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection"/>
@ -65,13 +38,8 @@
</object>
</child>
</object>
</child>
</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</template>
</interface>

View File

@ -1,142 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="gladecambalache" version="0.0"/>
<requires lib="gtk" version="4.0"/>
<object class="CmbTextBuffer" id="authors"/>
<object class="CmbTextBuffer" id="comment"/>
<object class="CmbTextBuffer" id="copyright"/>
<object class="CmbTextBuffer" id="description"/>
<!-- n-columns=2 n-rows=8 -->
<template class="CmbUIEditor" parent="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">4</property>
<property name="column-spacing">3</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Filename:</property>
<property name="label" translatable="1">Filename:</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Description:</property>
<property name="label" translatable="1">Description:</property>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Copyright:</property>
<property name="label" translatable="1">Copyright:</property>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Authors:</property>
<property name="label" translatable="1">Authors:</property>
<layout>
<property name="column">0</property>
<property name="row">4</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Domain:</property>
<property name="label" translatable="1">Domain:</property>
<layout>
<property name="column">0</property>
<property name="row">5</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="CmbEntry" id="filename">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="placeholder-text" translatable="yes">&lt;file name relative to project&gt;</property>
<property name="placeholder-text" translatable="1">&lt;file name relative to project&gt;</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="CmbEntry" id="translation_domain">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="halign">start</property>
<property name="width-chars">16</property>
<property name="placeholder-text" translatable="yes">&lt;translation domain&gt;</property>
<property name="placeholder-text" translatable="1">&lt;translation domain&gt;</property>
<layout>
<property name="column">1</property>
<property name="row">5</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<property name="focusable">1</property>
<property name="min-content-height">96</property>
<property name="max-content-height">256</property>
<child>
<property name="child">
<object class="GtkTextView">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focusable">1</property>
<property name="buffer">description</property>
</object>
</child>
</property>
<layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<property name="focusable">1</property>
<property name="min-content-height">96</property>
<property name="max-content-height">256</property>
<child>
<property name="child">
<object class="GtkTextView">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focusable">1</property>
<property name="buffer">authors</property>
</object>
</child>
</property>
<layout>
<property name="column">1</property>
<property name="row">4</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="CmbToplevelChooser" id="template_id">
@ -144,129 +122,102 @@
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="derivable-only">True</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Template:</property>
<property name="label" translatable="1">Template:</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="valign">end</property>
<property name="vexpand">True</property>
<property name="vexpand">1</property>
<property name="spacing">4</property>
<child>
<object class="GtkButton" id="remove_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="focusable">1</property>
<property name="receives-default">1</property>
<signal name="clicked" handler="on_remove_button_clicked" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">app-remove-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="export_button">
<property name="label" translatable="yes">Export</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Export</property>
<property name="label" translatable="1">Export</property>
<property name="focusable">1</property>
<property name="receives-default">1</property>
<property name="tooltip-text" translatable="1">Export</property>
<property name="halign">start</property>
<signal name="clicked" handler="on_export_button_clicked" swapped="no"/>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">2</property>
</packing>
</child>
<layout>
<property name="column">0</property>
<property name="row">7</property>
<property name="column-span">2</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">7</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<property name="focusable">1</property>
<property name="min-content-height">96</property>
<property name="max-content-height">256</property>
<child>
<property name="child">
<object class="GtkTextView">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focusable">1</property>
<property name="buffer">copyright</property>
</object>
</child>
</property>
<layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Comment:</property>
<property name="label" translatable="1">Comment:</property>
<layout>
<property name="column">0</property>
<property name="row">6</property>
</layout>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">6</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<property name="focusable">1</property>
<property name="min-content-height">96</property>
<property name="max-content-height">256</property>
<child>
<property name="child">
<object class="GtkTextView">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="focusable">1</property>
<property name="buffer">comment</property>
</object>
</child>
</property>
<layout>
<property name="column">1</property>
<property name="row">6</property>
</layout>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">6</property>
</packing>
</child>
</template>
</interface>

View File

@ -24,6 +24,7 @@
from gi.repository import GObject, Gtk
from .cmb_ui import CmbUI
from . import utils
class CmbUIRequiresEditor(Gtk.Grid):
@ -71,7 +72,7 @@ class CmbUIRequiresEditor(Gtk.Grid):
def __update(self):
self.__combos = {}
for child in self.get_children():
for child in utils.widget_get_children(self):
self.remove(child)
if self.__object is None:

View File

@ -1,7 +1,7 @@
#
# CmbView - Cambalache View
#
# Copyright (C) 2021 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
@ -27,20 +27,21 @@ import socket
import time
import warnings
from gi.repository import GObject, GLib, Gtk, WebKit2
from gi.repository import GObject, GLib, Gtk, WebKit
from . import config
from .cmb_ui import CmbUI
from .cmb_object import CmbObject
from .cmb_context_menu import CmbContextMenu
from . import utils
from cambalache import getLogger, _
logger = getLogger(__name__)
basedir = os.path.dirname(__file__) or "."
GObject.type_ensure(WebKit2.Settings.__gtype__)
GObject.type_ensure(WebKit2.WebView.__gtype__)
GObject.type_ensure(WebKit.Settings.__gtype__)
GObject.type_ensure(WebKit.WebView.__gtype__)
class CmbProcess(GObject.Object):
@ -111,7 +112,7 @@ class CmbProcess(GObject.Object):
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_view.ui")
class CmbView(Gtk.Stack):
class CmbView(Gtk.Box):
__gtype_name__ = "CmbView"
__gsignals__ = {
@ -121,6 +122,7 @@ class CmbView(Gtk.Stack):
preview = GObject.Property(type=bool, default=False, flags=GObject.ParamFlags.READWRITE)
stack = Gtk.Template.Child()
webview = Gtk.Template.Child()
text_view = Gtk.Template.Child()
@ -129,6 +131,7 @@ class CmbView(Gtk.Stack):
self.__restart_project = None
self.__ui_id = 0
self.__theme = None
self.__dark = False
self.menu = self.__create_context_menu()
@ -145,23 +148,12 @@ class CmbView(Gtk.Stack):
self.__port = None
self.__merengue_last_exit = None
context = self.get_style_context()
context.connect("changed", lambda ctx: self.__update_webview_bg())
if self.__broadwayd_bin is None:
logger.warning("broadwayd not found, Gtk 3 workspace wont work.")
if self.__gtk4_broadwayd_bin is None:
logger.warning("gtk4-broadwayd not found, Gtk 4 workspace wont work.")
GObject.Object.bind_property(
self,
"gtk-theme",
self.menu,
"gtk-theme",
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
)
self.connect("notify::preview", self.__on_preview_notify)
def do_destroy(self):
@ -174,23 +166,18 @@ class CmbView(Gtk.Stack):
def __evaluate_js(self, script):
self.webview.evaluate_javascript(script, -1, None, None, None, None, None, None)
def __update_webview_bg(self):
context = self.get_style_context()
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
bg = context.get_background_color(Gtk.StateFlags.NORMAL)
self.__evaluate_js(f"document.body.style.background = '{bg.to_string()}';")
def _set_dark_mode(self, dark):
self.__dark = dark
self.__evaluate_js(f"document.body.style.background = '{'#222' if dark else 'inherit'}';")
def __on_load_changed(self, webview, event):
if event != WebKit2.LoadEvent.FINISHED:
if event != WebKit.LoadEvent.FINISHED:
return
self.__update_webview_bg()
self._set_dark_mode(self.__dark)
# Disable alert() function used when broadwayd get disconnected
# Monkey patch setupDocument() to avoid disabling document.oncontextmenu
# Monkey pat ch setupDocument() to avoid disabling document.oncontextmenu
self.__evaluate_js(
"""
window.alert = function (message) {
@ -232,7 +219,7 @@ window.setupDocument = function (document) {
def __update_view(self):
if self.__project is not None and self.__ui_id > 0:
if self.props.visible_child_name == "ui_xml":
if self.stack.props.visible_child_name == "ui_xml":
ui = self.__get_ui_xml(self.__ui_id)
self.text_view.buffer.set_text(ui)
return
@ -468,8 +455,8 @@ window.setupDocument = function (document) {
self.__merengue_command("gtk_settings_set", args={"property": "gtk-theme-name", "value": theme})
@Gtk.Template.Callback("on_context_menu")
def __on_context_menu(self, webview, menu, e, hit_test_result):
self.menu.popup_at(e.x, e.y)
def __on_context_menu(self, webview, menu, hit_test_result):
self.menu.popup_at(*utils.get_pointer(self))
return True
def __webview_set_msg(self, msg):
@ -494,25 +481,20 @@ window.setupDocument = function (document) {
if bin is not None:
self.__webview_set_msg(_("Workspace not available\n{bin} executable not found").format(bin=bin))
def __on_inspect_button_clicked(self, button):
self.props.visible_child_name = "ui_xml"
def inspect(self):
self.stack.props.visible_child_name = "ui_xml"
self.__update_view()
def __on_restart_button_clicked(self, button):
def restart_workspace(self):
self.__restart_project = self.__project
self.project = None
def __create_context_menu(self):
retval = CmbContextMenu(relative_to=self)
retval = CmbContextMenu()
retval.set_parent(self)
restart = Gtk.ModelButton(text=_("Restart workspace"), visible=True)
restart.connect("clicked", self.__on_restart_button_clicked)
inspect = Gtk.ModelButton(text=_("Inspect UI definition"), visible=True)
inspect.connect("clicked", self.__on_inspect_button_clicked)
retval.main_box.add(restart)
retval.main_box.add(inspect)
retval.main_section.append(_("Restart workspace"), "win.workspace_restart")
retval.main_section.append(_("Inspect UI definition"), "win.inspect")
return retval

View File

@ -1,87 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="webkit2gtk" version="2.28"/>
<object class="WebKitSettings" type-func="webkit_settings_get_type" id="settings">
<requires lib="gtk" version="4.0"/>
<object class="WebKitSettings" id="settings">
<property name="enable-html5-local-storage">False</property>
<property name="enable-html5-database">False</property>
<property name="enable-java">False</property>
<property name="enable-fullscreen">False</property>
<property name="enable-webaudio">False</property>
<property name="media-playback-allows-inline">False</property>
<property name="user-agent">Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15</property>
<property name="enable-accelerated-2d-canvas">True</property>
<property name="enable-media">False</property>
</object>
<template class="CmbView" parent="GtkStack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="events">GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
<template class="CmbView" parent="GtkBox">
<child>
<object class="GtkStack" id="stack">
<property name="hexpand">true</property>
<property name="transition-duration">300</property>
<property name="transition-type">crossfade</property>
<child>
<object class="WebKitWebView" type-func="webkit_web_view_get_type" id="webview">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="settings">settings</property>
<signal name="context-menu" handler="on_context_menu" swapped="no"/>
<child>
<placeholder/>
</child>
</object>
<packing>
<object class="GtkStackPage">
<property name="name">ui_view</property>
<property name="title" translatable="yes">Project View</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow">
<property name="title" translatable="1">Project View</property>
<property name="child">
<object class="WebKitWebView" id="webview">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<property name="settings">settings</property>
<signal name="context-menu" handler="on_context_menu" swapped="no"/>
<child>
<object class="CmbSourceView" id="text_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="editable">False</property>
<property name="cursor-visible">False</property>
<property name="lang">xml</property>
<placeholder/>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">ui_xml</property>
<property name="title" translatable="1">UI Definition</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">1</property>
<property name="focusable">1</property>
<child>
<object class="CmbSourceView" id="text_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="editable">False</property>
<property name="cursor-visible">False</property>
<property name="lang">xml</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkStackSwitcher">
<property name="halign">center</property>
<property name="margin-top">4</property>
<property name="margin-bottom">4</property>
<property name="stack">CmbView</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkStackSwitcher">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">center</property>
<property name="margin-top">4</property>
<property name="margin-bottom">4</property>
<property name="stack">CmbView</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</property>
</object>
<packing>
<property name="name">ui_xml</property>
<property name="title" translatable="yes">UI Definition</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</template>
</interface>

View File

@ -35,23 +35,30 @@ class CmbColorEntry(Gtk.Box):
super().__init__(**kwargs)
self.entry = Gtk.Entry(visible=True, width_chars=14, editable=False)
self.button = Gtk.ColorButton(visible=True, use_alpha=True)
self.button = Gtk.ColorDialogButton(
visible=True,
dialog=Gtk.ColorDialog()
)
self.__default_color = self.button.props.color
#self.__default_color = self.button.props.color
self.__default_rgba = self.button.props.rgba
self.pack_start(self.entry, False, True, 0)
self.pack_start(self.button, False, True, 4)
self.append(self.entry)
self.append(self.button)
self.button.connect("color-set", self.__on_color_set)
# FIXME: GTK4
self.button.connect("notify::rgba", self.__on_color_set)
self.entry.connect("icon-press", self.__on_entry_icon_pressed)
def __on_entry_icon_pressed(self, widget, icon_pos, event):
self.cmb_value = None
def __on_color_set(self, obj):
def __on_color_set(self, obj, pspec):
print(self.use_color, self.button.props.rgba.to_string())
if self.use_color:
self.cmb_value = self.button.props.color.to_string() if self.button.props.color else None
pass
# FIXME: GTK4
#self.cmb_value = self.button.props.color.to_string() if self.button.props.color else None
else:
self.cmb_value = self.button.props.rgba.to_string() if self.button.props.rgba else None
@ -75,7 +82,8 @@ class CmbColorEntry(Gtk.Box):
if value:
valid, color = Gdk.Color.parse(value)
self.button.set_color(color if valid else self.__default_color)
# FIXME: GTK4
#self.button.set_color(color if valid else self.__default_color)
else:
rgba = Gdk.RGBA()

View File

@ -37,8 +37,9 @@ class CmbEntry(Gtk.Entry):
self.props.secondary_icon_name = "document-edit-symbolic"
self.connect("icon-press", self.__on_icon_pressed)
def __on_icon_pressed(self, widget, icon_pos, event):
popover = CmbTranslatablePopover(relative_to=self)
def __on_icon_pressed(self, widget, icon_pos):
popover = CmbTranslatablePopover()
popover.set_parent(self)
popover.bind_properties(self._target)
popover.popup()

View File

@ -47,14 +47,16 @@ class CmbFlagsEntry(Gtk.Entry):
self.__init_popover()
def __init_popover(self):
self._popover = Gtk.Popover(relative_to=self)
self._popover = Gtk.Popover()
self._popover.set_parent(self)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
box.pack_start(Gtk.Label(label=f"<b>{self.info.type_id}</b>", use_markup=True), False, True, 4)
box.pack_start(Gtk.Separator(), False, False, 0)
box.prepend(Gtk.Label(label=f"<b>{self.info.type_id}</b>", use_markup=True))
box.prepend(Gtk.Separator())
sw = Gtk.ScrolledWindow(hscrollbar_policy=Gtk.PolicyType.NEVER, propagate_natural_height=True, max_content_height=360)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
sw.add(vbox)
box.pack_start(sw, True, True, 0)
sw.set_child(vbox)
box.prepend(sw)
for row in self.info.flags:
flag = row[self.text_column]
@ -62,18 +64,17 @@ class CmbFlagsEntry(Gtk.Entry):
check = Gtk.CheckButton(label=flag)
check.connect("toggled", self.__on_check_toggled, flag_id)
vbox.pack_start(check, False, True, 4)
vbox.prepend(check)
self._checks[flag_id] = check
box.show_all()
self._popover.add(box)
self._popover.set_child(box)
def __on_check_toggled(self, check, flag_id):
self.flags[flag_id] = check.props.active
self.props.text = self.__to_string()
self.notify("cmb-value")
def __on_icon_release(self, obj, pos, event):
def __on_icon_release(self, obj, pos):
self._popover.popup()
def __to_string(self):

View File

@ -24,7 +24,7 @@
import os
from cambalache import _
from gi.repository import GdkPixbuf, GObject, Gtk, Pango
from gi.repository import GLib, Gio, GdkPixbuf, GObject, Gdk, Gtk, Pango
from .cmb_entry import CmbEntry
from .icon_naming_spec import standard_icon_context, standard_icon_names
@ -48,6 +48,8 @@ class CmbIconNameEntry(CmbEntry):
# Model, store it in a Python class variable to share between all instances
icon_model = None
iconlist = []
def __init__(self, **kwargs):
self._filters = {}
@ -91,32 +93,56 @@ class CmbIconNameEntry(CmbEntry):
iconlist = []
theme = Gtk.IconTheme.get_default()
theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default())
for context in theme.list_contexts():
for icon in theme.list_icons(context):
iconlist.append((icon, context, icon in standard_icon_names))
# FIXME: get the context/category of each icon
for icon in theme.get_icon_names():
iconlist.append((icon, "cmb_all", icon in standard_icon_names))
for icon, context, standard in sorted(iconlist, key=lambda i: i[0].lower()):
if icon.endswith(".symbolic"):
continue
info = theme.lookup_icon(icon, 32, Gtk.IconLookupFlags.FORCE_SIZE)
symbolic = info.is_symbolic()
icon_paintable = theme.lookup_icon(icon, None, 32, 1, Gtk.TextDirection.NONE, Gtk.IconLookupFlags.PRELOAD)
symbolic = icon_paintable.is_symbolic()
if not os.path.exists(info.get_filename()):
icon_file = icon_paintable.get_file()
if icon_file is None:
continue
icon_path = icon_file.get_path()
if icon_path is None or not os.path.exists(icon_path):
continue
standard_symbolic = symbolic and icon.removesuffix("-symbolic") in standard_icon_names
iter = cls.icon_model.append(
[icon, icon if standard else f"<i>{icon}</i>", context, standard, symbolic, standard_symbolic, None]
)
info.load_icon_async(None, cls.__load_icon_finish, iter)
try:
iter = cls.icon_model.append(
[icon, icon if standard else f"<i>{icon}</i>", context, standard, symbolic, standard_symbolic, None]
)
cls.iconlist.append((icon_file, iter))
except Exception as e:
print(e)
# Kickoff async loading
file, iter = cls.iconlist.pop()
file.read_async(GLib.PRIORITY_DEFAULT, None, cls.__load_file_finish, iter)
@classmethod
def __load_icon_finish(cls, info, res, data):
cls.icon_model[data][6] = info.load_icon_finish(res)
def __load_file_finish(cls, obj, res, iter):
stream = obj.read_finish(res)
GdkPixbuf.Pixbuf.new_from_stream_at_scale_async(stream, 32, 32, True, None, cls.__load_icon_finish, iter)
@classmethod
def __load_icon_finish(cls, obj, res, iter):
try:
cls.icon_model[iter][cls.COL_PIXBUF] = GdkPixbuf.Pixbuf.new_from_stream_finish(res)
except Exception as e:
print(e)
if len(cls.iconlist):
file, iter = cls.iconlist.pop()
file.read_async(GLib.PRIORITY_DEFAULT, None, cls.__load_file_finish, iter)
def __model_filter_func(self, model, iter, data):
if self.standard_only and self.symbolic_only:
@ -146,20 +172,23 @@ class CmbIconNameEntry(CmbEntry):
else:
self.cmb_value = None
def __on_icon_pressed(self, widget, icon_pos, event):
def __on_icon_pressed(self, widget, icon_pos):
# Create popover with icon chooser
popover = Gtk.Popover(relative_to=self)
popover = Gtk.Popover()
popover.set_parent(self)
hbox = Gtk.Box(visible=True)
vbox = Gtk.Box(visible=True, orientation=Gtk.Orientation.VERTICAL, vexpand=True)
stack = Gtk.Stack(visible=True, transition_type=Gtk.StackTransitionType.CROSSFADE)
sidebar = Gtk.StackSidebar(visible=True, stack=stack, vexpand=True)
vbox.pack_start(sidebar, True, True, 4)
hbox.pack_start(vbox, False, True, 4)
hbox.pack_start(stack, True, True, 4)
vbox.append(sidebar)
hbox.append(vbox)
hbox.append(stack)
theme = Gtk.IconTheme.get_default()
theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default())
sorted_contexts = sorted(theme.list_contexts())
# sorted_contexts = sorted(theme.list_contexts())
sorted_contexts = []
sorted_contexts.insert(0, "cmb_all")
# Add one icon view per context
@ -175,7 +204,7 @@ class CmbIconNameEntry(CmbEntry):
sw = Gtk.ScrolledWindow(visible=True, min_content_width=600, min_content_height=480)
view = Gtk.IconView(visible=True, model=filter, pixbuf_column=self.COL_PIXBUF, text_column=self.COL_ICON_NAME)
view.connect("selection-changed", self.__on_view_selection_changed)
sw.add(view)
sw.set_child(view)
stack.add_titled(sw, context, standard_icon_context.get(context, context))
# Add filters
@ -185,8 +214,8 @@ class CmbIconNameEntry(CmbEntry):
self, prop, check, "active", GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL
)
check.connect_after("notify::active", self.__on_check_active_notify)
vbox.pack_start(check, False, True, 4)
vbox.append(check)
popover.get_style_context().add_class("cmb-icon-chooser")
popover.add(hbox)
popover.add_css_class("cmb-icon-chooser")
popover.set_child(hbox)
popover.popup()

View File

@ -106,7 +106,7 @@ class CmbObjectChooser(Gtk.Entry):
parent.project.add_object(parent.ui_id, info.type_id, parent_id=parent.object_id, inline_property=self.prop.property_id)
self.__update_icons()
def __on_icon_pressed(self, widget, icon_pos, event):
def __on_icon_pressed(self, widget, icon_pos):
parent = self.parent
project = parent.project
prop = self.prop
@ -116,7 +116,8 @@ class CmbObjectChooser(Gtk.Entry):
project.remove_object(obj)
self.__update_icons()
else:
chooser = CmbTypeChooserPopover(relative_to=self, parent_type_id=parent.type_id, derived_type_id=prop.info.type_id)
chooser = CmbTypeChooserPopover(parent_type_id=parent.type_id, derived_type_id=prop.info.type_id)
chooser.set_parent(self)
chooser.project = project
chooser.connect("type-selected", self.__on_type_selected)
chooser.popup()

View File

@ -33,7 +33,6 @@ class CmbTextView(Gtk.ScrolledWindow):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.props.shadow_type = Gtk.ShadowType.IN
self.props.height_request = 64
self.buffer = CmbTextBuffer()
self.view = Gtk.TextView(visible=True, buffer=self.buffer)
@ -46,4 +45,4 @@ class CmbTextView(Gtk.ScrolledWindow):
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
)
self.add(self.view)
self.set_child(self.view)

View File

@ -35,14 +35,13 @@ class CmbTranslatablePopover(Gtk.Popover):
super().__init__(**kwargs)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
box.pack_start(Gtk.Label(label=_("<b>Translation</b>"), use_markup=True), False, True, 4)
box.pack_start(Gtk.Separator(), False, False, 0)
box.append(Gtk.Label(label=_("<b>Translation</b>"), use_markup=True))
box.append(Gtk.Separator())
self._translation = CmbTranslatableWidget()
box.pack_start(self._translation, False, False, 0)
box.append(self._translation)
box.show_all()
self.add(box)
self.set_child(box)
def bind_properties(self, target):
self._translation.bind_properties(target)

View File

@ -1,13 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<requires lib="gtk" version="4.0"/>
<object class="GtkTextBuffer" id="buffer_comments"/>
<object class="GtkTextBuffer" id="buffer_context"/>
<object class="GtkTextBuffer" id="buffer_text"/>
<template class="CmbTranslatableWidget" parent="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">4</property>
<property name="margin-end">4</property>
<property name="margin-top">4</property>
@ -16,126 +13,70 @@
<property name="spacing">4</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Text:</property>
<property name="label" translatable="1">Text:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="width-request">300</property>
<property name="height-request">60</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<property name="focusable">1</property>
<property name="child">
<object class="GtkTextView" id="text_view_value">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="border-width">2</property>
<property name="focusable">1</property>
<property name="buffer">buffer_text</property>
<property name="accepts-tab">False</property>
<property name="accepts-tab">0</property>
</object>
</child>
</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="check_button_translatable">
<property name="label" translatable="yes">translatable</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="label" translatable="1">translatable</property>
<property name="focusable">1</property>
<property name="halign">start</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin-top">8</property>
<property name="label" translatable="yes">Translation context:</property>
<property name="label" translatable="1">Translation context:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="height-request">60</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<property name="focusable">1</property>
<property name="child">
<object class="GtkTextView" id="text_view_context">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="border-width">2</property>
<property name="focusable">1</property>
<property name="buffer">buffer_context</property>
<property name="accepts-tab">False</property>
<property name="accepts-tab">0</property>
</object>
</child>
</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin-top">8</property>
<property name="label" translatable="yes">Comments for translators:</property>
<property name="label" translatable="1">Comments for translators:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="height-request">60</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<property name="focusable">1</property>
<property name="child">
<object class="GtkTextView" id="text_view_comments">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="border-width">2</property>
<property name="focusable">1</property>
<property name="buffer">buffer_comments</property>
<property name="accepts-tab">False</property>
<property name="accepts-tab">0</property>
</object>
</child>
</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">7</property>
</packing>
</child>
</template>
</interface>

View File

@ -136,8 +136,8 @@ class MrgApplication(Gtk.Application):
# FIXME: object_id could be reused for a different object type
# if you undo the creation of a widget and create a different type
# As a workaround if the types do not match we create a new controller
# This could be fixed if we alway auto increment object_id but then
# we would have to clean up unussed controllers
# This could be fixed if we always auto increment object_id but then
# we would have to clean up unused controllers
if pspec is None or pspec.value_type != obj.__gtype__:
controller = self.registry.new_controller_for_type(obj.__gtype__, self)

View File

@ -100,15 +100,20 @@ class MrgGtkWidget(MrgController):
if self.object is None:
return
if self.selected:
self.object.get_style_context().add_class("merengue_selected")
else:
self.object.get_style_context().remove_class("merengue_selected")
# Update toplevel backdrop state
if Gtk.MAJOR_VERSION == 4:
if self.selected:
self.object.add_css_class("merengue_selected")
else:
self.object.remove_css_class("merengue_selected")
toplevel = self.object.get_root()
else:
if self.selected:
self.object.get_style_context().add_class("merengue_selected")
else:
self.object.get_style_context().remove_class("merengue_selected")
toplevel = self.object.get_toplevel()
if toplevel:

View File

@ -80,8 +80,10 @@ class MrgGtkWindow(MrgGtkBin):
self.object.show_all()
# Add gtk version CSS class
gtkversion = "gtk4" if Gtk.MAJOR_VERSION == 4 else "gtk3"
self.object.get_style_context().add_class(gtkversion)
if Gtk.MAJOR_VERSION == 4:
self.object.add_css_class("gtk4")
else:
self.object.get_style_context().add_class("gtk3")
self._restore_state()

View File

@ -48,15 +48,17 @@ def version_cmp_str(a, b):
def unset_scroll_event(widget):
def ignore_scroll_event(widget, event):
Gtk.propagate_event(widget.get_parent(), event)
return True
pass
# FIXME: GTK4
# def ignore_scroll_event(widget, event):
# Gtk.propagate_event(widget.get_parent(), event)
# return True
events = widget.get_events()
widget.set_events(events & ~(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK))
# events = widget.get_events()
# widget.set_events(events & ~(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK))
if isinstance(widget, Gtk.ComboBox):
widget.connect("scroll-event", ignore_scroll_event)
# if isinstance(widget, Gtk.ComboBox):
# widget.connect("scroll-event", ignore_scroll_event)
def get_version_warning(target, version, deprecated_version, this):
@ -69,3 +71,32 @@ def get_version_warning(target, version, deprecated_version, this):
return f"UI targets {target} but {this} was deprecated in {deprecated_version}"
return None
def widget_get_children(widget):
retval = []
child = widget.get_first_child()
while child is not None:
retval.append(child)
child = child.get_next_sibling()
return retval
def get_pointer(widget):
root = widget.get_root()
pointer = widget.get_display().get_default_seat().get_pointer()
valid, x, y, mask = root.get_surface().get_device_position(pointer)
if valid:
return root.translate_coordinates(widget, x, y)
return (None, None)
def get_pointing_to(widget):
r = Gdk.Rectangle()
r.x, r.y = get_pointer(widget)
r.width = r.height = 0
return r

View File

@ -6,7 +6,7 @@ project(
# File format version follows app version and only changes when there is a
# change that prevents older versions to load it.
fileformatversion = '0.13.1'
fileformatversion = '0.17.0'
python = import('python')
python_bin = python.find_installation('python3')

View File

@ -61,7 +61,7 @@ def close_compositor():
# Make sure the right gtk version is loaded
gi.require_version("Gtk", "3.0")
gi.require_version("Gtk", "4.0")
# Make sure we can run Cambalache from sources
cmb_init_dev()

View File

@ -28,28 +28,23 @@ import struct
from gi.repository import GLib, Gtk
# Based on Gtk sources gtk-reftest.c
# Based on Gtk sources gtktestutils.c gtk_test_widget_wait_for_draw()
def wait_for_drawing(window):
def on_window_draw(widget, cr, loop):
loop.quit()
return False
done = { "done": False }
main_loop = GLib.MainContext.default()
def quit_when_idle(loop):
loop.quit()
def quit_main_loop_callback(widget, frame_clock, done):
done["done"] = True
print("BBBBBBBBB")
main_loop.wakeup()
print("CCCCCCCCCCCCCC")
return GLib.SOURCE_REMOVE
loop = GLib.MainLoop()
window.add_tick_callback(quit_main_loop_callback, done)
# We wait until the widget is drawn for the first time.
# We are running in a dedicated compositor so the window should not be obstructed by other windows
window.connect("draw", on_window_draw, loop)
loop.run()
window.disconnect_by_func(on_window_draw)
# give the WM/server some time to sync. They need it.
window.get_display().sync()
GLib.timeout_add(500, quit_when_idle, loop)
loop.run()
print("AAAAAAAAAAAA")
while not done["done"]:
main_loop.iteration(True)
def surface_write_ppm(surface, path):
@ -71,13 +66,20 @@ def window_screenshot(window):
# Wait for window to finish drawing
wait_for_drawing(window)
w = window.get_allocated_width()
h = window.get_allocated_height()
paintable = Gtk.WidgetPaintable.new(window)
snapshot = Gtk.Snapshot()
w = paintable.get_intrinsic_width()
h = paintable.get_intrinsic_height()
# Draw widget to cairo surface
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
paintable.snapshot(snapshot, w, h)
node = snapshot.to_node()
cr = cairo.Context(surface)
window.draw(cr)
node.draw(cr)
surface.flush()
@ -124,26 +126,36 @@ def mean_squared_error(original, screenshot, ignore_color=None):
def process_all_pending_gtk_events():
while Gtk.events_pending():
Gtk.main_iteration_do(False)
main_loop = GLib.MainContext.default()
while main_loop.pending():
main_loop.iteration(False)
def __get_children(obj):
if obj is None:
return []
retval = []
child = obj.get_first_child()
while child is not None:
retval.append(child)
child = child.get_next_sibling()
return retval
def find_by_buildable_id(widget, name):
retval = None
if isinstance(widget, Gtk.Buildable) and Gtk.Buildable.get_name(widget) == name:
if isinstance(widget, Gtk.Buildable) and Gtk.Buildable.get_buildable_id(widget) == name:
return widget
if not isinstance(widget, Gtk.Container):
return None
for child in __get_children(widget):
retval = find_by_buildable_id(child, name)
if retval:
return retval
for child in widget.get_children():
if isinstance(child, Gtk.Container):
retval = find_by_buildable_id(child, name)
if retval:
return retval
if isinstance(child, Gtk.Buildable) and Gtk.Buildable.get_name(child) == name:
if isinstance(child, Gtk.Buildable) and Gtk.Buildable.get_buildable_id(child) == name:
return child
return retval
@ -159,8 +171,9 @@ def cmb_create_app():
window = None
# Spin until we get the main window
while Gtk.events_pending() and not window:
Gtk.main_iteration_do(False)
main_loop = GLib.MainContext.default()
while main_loop.pending() and not window:
main_loop.iteration(False)
# Get window if any
windows = app.get_windows()