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 # 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 # This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public # modify it under the terms of the GNU Lesser General Public
@ -28,10 +28,10 @@ import builtins
from . import config from . import config
gi.require_version("Gdk", "3.0") gi.require_version("Gdk", "4.0")
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "4.0")
gi.require_version("GtkSource", "4") gi.require_version("GtkSource", "5")
gi.require_version("WebKit2", "4.1") gi.require_version("WebKit", "6.0")
# Ensure _() builtin # Ensure _() builtin
if "_" not in builtins.__dict__: if "_" not in builtins.__dict__:
@ -54,8 +54,9 @@ resource._register()
provider = Gtk.CssProvider() provider = Gtk.CssProvider()
provider.load_from_resource("/ar/xjuan/Cambalache/cambalache.css") 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) display = Gdk.Display.get_default()
Gtk.IconTheme.get_default().add_resource_path("/ar/xjuan/Cambalache/icons") 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): 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 { 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 { CmbWindow.dark .logo {
color: white; 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 { CmbWindow label.message {
@ -56,7 +43,7 @@ CmbWindow.dark label.message {
background-color: rgba(255, 255, 255, .6); background-color: rgba(255, 255, 255, .6);
} }
popover.cmb-tutor { popover.cmb-tutor > * {
padding: 1em; padding: 1em;
} }
@ -65,13 +52,23 @@ popover.cmb-tutor label {
font-weight: bold; font-weight: bold;
} }
popover.cmb-tutor image {
padding-right: 1em;
-gtk-icon-size: 48px;
}
button.cmb-tutor-highlight, button.cmb-tutor-highlight,
modelbutton.cmb-tutor-highlight, modelbutton.cmb-tutor-highlight,
buttonbox.cmb-tutor-highlight > button, buttonbox.cmb-tutor-highlight > button,
stackswitcher.cmb-tutor-highlight > button, stackswitcher.cmb-tutor-highlight > button,
entry.cmb-tutor-highlight, entry.cmb-tutor-highlight,
treeview.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; box-shadow: inset 0px 0px 6px @theme_selected_bg_color;
transition: box-shadow .75s ease; transition: box-shadow .75s ease;
} }
CmbView.cmb-tutor-highlight {
padding: 6px;
}

View File

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

View File

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

View File

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

View File

@ -36,9 +36,9 @@ intro = [
(_("Common actions like Undo"), "undo_button", 4), (_("Common actions like Undo"), "undo_button", 4),
(_("Redo"), "redo_button", 2), (_("Redo"), "redo_button", 2),
(_("Add new UI file"), "add_button", 3), (_("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), (_("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"), (_("Create a project to continue"), "intro_button", 2, "add-project"),
(_("Great!"), "intro_button", 2), (_("Great!"), "intro_button", 2),
( (
@ -66,14 +66,21 @@ intro = [
(_("Quite easy! Isn't it?"), "intro_button", 3), (_("Quite easy! Isn't it?"), "intro_button", 3),
( (
_("Once you finish, you can export all UI files to xml here"), _("Once you finish, you can export all UI files to xml here"),
"export_all", _("Export all"),
5, 5,
"main-menu", "main-menu",
CmbTutorPosition.LEFT, 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"), _("That is all for now.\nIf you find Cambalache useful please consider donating"),
"donate", _("Donate"),
7, 7,
"donate", "donate",
CmbTutorPosition.LEFT, CmbTutorPosition.LEFT,

View File

@ -1,7 +1,7 @@
# #
# CmbWindow # 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 # This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public # 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 .cmb_tutor import CmbTutor, CmbTutorState
from . import cmb_tutorial 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__) logger = getLogger(__name__)
@ -39,15 +39,17 @@ logger = getLogger(__name__)
class CmbWindow(Gtk.ApplicationWindow): class CmbWindow(Gtk.ApplicationWindow):
__gtype_name__ = "CmbWindow" __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() open_filter = Gtk.Template.Child()
import_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() headerbar = Gtk.Template.Child()
subtitle = Gtk.Template.Child()
recent_menu = Gtk.Template.Child()
undo_button = Gtk.Template.Child() undo_button = Gtk.Template.Child()
redo_button = Gtk.Template.Child() redo_button = Gtk.Template.Child()
stack = Gtk.Template.Child() stack = Gtk.Template.Child()
@ -85,62 +87,83 @@ class CmbWindow(Gtk.ApplicationWindow):
# Tutor widgets # Tutor widgets
intro_button = Gtk.Template.Child() intro_button = Gtk.Template.Child()
main_menu = Gtk.Template.Child() menu_button = Gtk.Template.Child()
export_all = Gtk.Template.Child()
# Settings # Settings
completed_intro = GObject.Property(type=bool, default=False, flags=GObject.ParamFlags.READWRITE) completed_intro = GObject.Property(type=bool, default=False, flags=GObject.ParamFlags.READWRITE)
MAXIMIZED = 1 << 2
FULLSCREEN = 1 << 4
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.__project = None self.__project = None
self.__last_saved_index = None self.__last_saved_index = None
self.__np_location = None
super().__init__(**kwargs) super().__init__(**kwargs)
self.editor_stack.set_size_request(420, -1) self.editor_stack.set_size_request(420, -1)
self.actions = {} self.actions = {}
self.open_button_box.props.homogeneous = False
self.import_button_box.props.homogeneous = False
for action in [ for action in [
"open", "about",
"create_new",
"new",
"undo",
"redo",
"intro",
"save",
"save_as",
"add_ui",
"add_css", "add_css",
"copy",
"paste",
"cut",
"delete",
"add_object", "add_object",
"add_object_toplevel", "add_object_toplevel",
"clear",
"add_placeholder", "add_placeholder",
"remove_placeholder",
"add_placeholder_row", "add_placeholder_row",
"remove_placeholder_row", "add_ui",
"import", "clear",
"export",
"close", "close",
"debug",
"show_workspace",
"donate",
"liberapay",
"patreon",
"contact", "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 = Gio.SimpleAction.new(action, None)
gaction.connect("activate", getattr(self, f"_on_{action}_activate")) gaction.connect("activate", getattr(self, f"_on_{action}_activate"))
self.actions[action] = gaction self.actions[action] = gaction
self.add_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 # Add global accelerators
action_map = [ action_map = [
("win.save", ["<Primary>s"]), ("win.save", ["<Primary>s"]),
@ -205,7 +228,7 @@ class CmbWindow(Gtk.ApplicationWindow):
) )
self.tutor = None self.tutor = None
self.turor_waiting_for_user_action = False self.tutor_waiting_for_user_action = False
self.__clipboard_enabled = True self.__clipboard_enabled = True
self.__message_timeout_id = None self.__message_timeout_id = None
@ -237,6 +260,14 @@ class CmbWindow(Gtk.ApplicationWindow):
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL, 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) @GObject.Property(type=CmbProject)
def project(self): def project(self):
return self.__project return self.__project
@ -246,7 +277,6 @@ class CmbWindow(Gtk.ApplicationWindow):
if self.__project is not None: if self.__project is not None:
self.__project.disconnect_by_func(self.__on_project_filename_notify) 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_selection_changed)
self.__project.disconnect_by_func(self.__on_project_filename_required)
self.__project.disconnect_by_func(self.__on_project_changed) self.__project.disconnect_by_func(self.__on_project_changed)
self.__project = project self.__project = project
@ -267,10 +297,9 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__on_project_filename_notify(None, None) self.__on_project_filename_notify(None, None)
self.__project.connect("notify::filename", self.__on_project_filename_notify) self.__project.connect("notify::filename", self.__on_project_filename_notify)
self.__project.connect("selection-changed", self.__on_project_selection_changed) 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) self.__project.connect("changed", self.__on_project_changed)
else: else:
self.headerbar.set_subtitle(None) self.subtitle.props.visible = False
self.__update_actions() self.__update_actions()
@ -279,15 +308,9 @@ class CmbWindow(Gtk.ApplicationWindow):
path = self.project.filename.replace(GLib.get_home_dir(), "~") path = self.project.filename.replace(GLib.get_home_dir(), "~")
else: else:
path = _("Untitled") path = _("Untitled")
self.headerbar.set_subtitle(path)
@Gtk.Template.Callback("on_about_dialog_delete_event") self.subtitle.props.visible = True
def __on_about_dialog_delete_event(self, widget, event): self.subtitle.props.label = path
return True
@Gtk.Template.Callback("on_about_dialog_response")
def __on_about_dialog_response(self, widget, response_id):
widget.hide()
@Gtk.Template.Callback("on_type_chooser_type_selected") @Gtk.Template.Callback("on_type_chooser_type_selected")
def __on_type_chooser_type_selected(self, popover, info): 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]: if obj and type(obj) not in [CmbObject, CmbUI]:
return 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 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: if obj:
parent_id = obj.object_id if isinstance(obj, CmbObject) else None parent_id = obj.object_id if isinstance(obj, CmbObject) else None
self.project.add_object(obj.ui_id, info.type_id, None, parent_id) 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") @Gtk.Template.Callback("on_view_placeholder_activated")
def __on_view_placeholder_activated(self, view, ui_id, object_id, layout, position, child_type): 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) 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 popover.project = self.project
@ -349,13 +369,6 @@ class CmbWindow(Gtk.ApplicationWindow):
) )
popover.popup() 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") @Gtk.Template.Callback("on_ui_editor_remove_ui")
def __on_ui_editor_remove_ui(self, editor): def __on_ui_editor_remove_ui(self, editor):
self.__remove_object_with_confirmation(editor.object) self.__remove_object_with_confirmation(editor.object)
@ -366,9 +379,10 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__remove_object_with_confirmation(editor.object) self.__remove_object_with_confirmation(editor.object)
return True return True
@Gtk.Template.Callback("on_window_set_focus") def __on_focus_widget_notify(self, obj, pspec):
def __on_window_set_focus(self, window, widget): widget = self.props.focus_widget
types = [Gtk.Entry, Gtk.TextView, Gtk.SpinButton]
types = [Gtk.Text, Gtk.TextView]
focused_widget_needs = True focused_widget_needs = True
for type in types: for type in types:
@ -413,16 +427,16 @@ class CmbWindow(Gtk.ApplicationWindow):
r, g, b = (linear(c.red), linear(c.green), linear(c.blue)) r, g, b = (linear(c.red), linear(c.green), linear(c.blue))
return 0.2126 * r + 0.7152 * g + 0.0722 * b return 0.2126 * r + 0.7152 * g + 0.0722 * b
ctx = self.get_style_context()
# Get foreground color # 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 foreground luminance is closer to 1 then the background must be dark
if luminance(fg) > 0.5: if luminance(fg) > 0.5:
ctx.add_class("dark") self.add_css_class("dark")
self.view._set_dark_mode(True)
else: else:
ctx.remove_class("dark") self.remove_css_class("dark")
self.view._set_dark_mode(False)
def __np_name_to_ui(self, binding, value): def __np_name_to_ui(self, binding, value):
if len(value): if len(value):
@ -549,20 +563,16 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__update_action_undo_redo() self.__update_action_undo_redo()
self.__update_action_add_object() self.__update_action_add_object()
def __file_open_dialog_new( def __file_open_dialog_new(self, title, filter_obj=None, accept_label=None):
self, title, action=Gtk.FileChooserAction.OPEN, filter_obj=None, select_multiple=False, accept_label=None dialog = Gtk.FileDialog(
): modal=True,
dialog = Gtk.FileChooserNative(
title=title, title=title,
transient_for=self, default_filter=filter_obj,
action=action,
filter=filter_obj,
select_multiple=select_multiple,
accept_label=accept_label, accept_label=accept_label,
) )
if self.project and self.project.filename: 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 return dialog
@ -580,18 +590,21 @@ class CmbWindow(Gtk.ApplicationWindow):
def present_message_to_user(self, message, secondary_text=None, details=None): def present_message_to_user(self, message, secondary_text=None, details=None):
dialog = Gtk.MessageDialog( dialog = Gtk.MessageDialog(
transient_for=self, transient_for=self,
flags=0,
message_type=Gtk.MessageType.INFO, message_type=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.OK, buttons=Gtk.ButtonsType.OK,
text=message, text=message,
secondary_text=secondary_text, secondary_text=secondary_text,
modal=True
) )
if details: if details:
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4) box = Gtk.Box(
orientation=Gtk.Orientation.VERTICAL,
spacing=4
)
for detail in details: for detail in details:
box.add( box.append(
Gtk.Label( Gtk.Label(
label=detail, label=detail,
halign=Gtk.Align.START, halign=Gtk.Align.START,
@ -602,12 +615,10 @@ class CmbWindow(Gtk.ApplicationWindow):
ellipsize=Pango.EllipsizeMode.END, ellipsize=Pango.EllipsizeMode.END,
) )
) )
dialog.props.message_area.append(box)
box.show_all() dialog.connect("response", lambda d, r: dialog.destroy())
dialog.props.message_area.add(box) dialog.present()
dialog.run()
dialog.destroy()
def import_file(self, filename): def import_file(self, filename):
if self.project is None: if self.project is None:
@ -688,25 +699,41 @@ class CmbWindow(Gtk.ApplicationWindow):
) )
def _on_open_activate(self, action, data): def _on_open_activate(self, action, data):
dialog = self.__file_open_dialog_new(_("Choose project to open"), filter_obj=self.open_filter) def dialog_callback(dialog, res):
if dialog.run() == Gtk.ResponseType.ACCEPT: try:
self.emit("open-project", dialog.get_filename(), None, None) 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): def _on_create_new_activate(self, action, data):
self.__set_page("new_project") self.__set_page("new_project")
self.set_focus(self.np_name_entry) self.set_focus(self.np_name_entry)
home = GLib.get_home_dir() if self.__np_location is None:
projects = os.path.join(home, "Projects") home = GLib.get_home_dir()
directory = projects if os.path.isdir(projects) else home 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): def _on_new_activate(self, action, data):
name = self.np_name_entry.props.text name = self.np_name_entry.props.text
location = self.np_location_chooser.get_filename() or "."
uiname = self.np_ui_entry.props.text uiname = self.np_ui_entry.props.text
filename = None filename = None
uipath = None uipath = None
@ -718,7 +745,7 @@ class CmbWindow(Gtk.ApplicationWindow):
if len(name): if len(name):
name, ext = os.path.splitext(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: if len(uiname) == 0:
uiname = self.np_ui_entry.props.placeholder_text uiname = self.np_ui_entry.props.placeholder_text
@ -728,7 +755,7 @@ class CmbWindow(Gtk.ApplicationWindow):
self.set_focus(self.np_name_entry) self.set_focus(self.np_name_entry)
return return
uipath = os.path.join(location, uiname) uipath = os.path.join(self.__np_location, uiname)
self.emit("open-project", filename, target_tk, uipath) self.emit("open-project", filename, target_tk, uipath)
self.__set_page("workspace" if self.project is not None else "cambalache") self.__set_page("workspace" if self.project is not None else "cambalache")
@ -743,30 +770,23 @@ class CmbWindow(Gtk.ApplicationWindow):
self.project.redo() self.project.redo()
self.__update_action_undo_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): 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): def _on_save_as_activate(self, action, data):
if self.project is None: if self.project is None:
return return
dialog = self.__file_open_dialog_new(_("Choose a new file to save the project"), Gtk.FileChooserAction.SAVE) dialog = self.__file_open_dialog_new(_("Choose a new file to save the project"))
if dialog.run() == Gtk.ResponseType.ACCEPT: dialog.save(self, None, self.__save_dialog_callback)
self.project.filename = dialog.get_filename()
self.__save_project()
dialog.destroy()
def _on_add_ui_activate(self, action, data): def _on_add_ui_activate(self, action, data):
if self.project is None: if self.project is None:
@ -785,19 +805,23 @@ class CmbWindow(Gtk.ApplicationWindow):
def __remove_object_with_confirmation(self, obj): def __remove_object_with_confirmation(self, obj):
dialog = Gtk.MessageDialog( dialog = Gtk.MessageDialog(
transient_for=self, transient_for=self,
flags=0, modal=True,
message_type=Gtk.MessageType.QUESTION, message_type=Gtk.MessageType.QUESTION,
buttons=Gtk.ButtonsType.YES_NO, buttons=Gtk.ButtonsType.YES_NO,
text=_("Do you really want to remove {name}?").format(name=obj.get_display_name()), text=_("Do you really want to remove {name}?").format(name=obj.get_display_name()),
) )
if dialog.run() == Gtk.ResponseType.YES: def on_dialog_response(dialog, response):
if type(obj) == CmbUI: if response == Gtk.ResponseType.YES:
self.project.remove_ui(obj) if type(obj) == CmbUI:
elif type(obj) == CmbCSS: self.project.remove_ui(obj)
self.project.remove_css(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): def _on_copy_activate(self, action, data):
if self.project: if self.project:
@ -891,30 +915,43 @@ class CmbWindow(Gtk.ApplicationWindow):
if self.project is None: if self.project is None:
return 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( 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: def __save(self):
filenames = dialog.get_filenames() if self.project.save():
dialog.destroy() self.__last_saved_index = self.project.history_index
self.__update_action_save()
self.emit("project-saved", self.project)
for filename in filenames: def save_project(self):
self.import_file(filename) if self.project is None:
else: return False
dialog.destroy()
def __save_project(self): if self.project.filename is None:
if self.project is not None: dialog = self.__file_open_dialog_new(_("Choose a file to save the project"))
if self.project.save(): dialog.save(self, None, self.__save_dialog_callback)
self.__last_saved_index = self.project.history_index return True
self.__update_action_save()
# Save project and update last saved index
self.__save()
return False
def _on_export_activate(self, action, data): def _on_export_activate(self, action, data):
if self.project is None: if self.project is None:
return return
self.__save_project() self.save_project()
n = self.project.export() n = self.project.export()
@ -931,7 +968,7 @@ class CmbWindow(Gtk.ApplicationWindow):
fd, filename = tempfile.mkstemp(".db", "cmb") fd, filename = tempfile.mkstemp(".db", "cmb")
self.project.db_move_to_fs(filename) 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): def _on_about_activate(self, action, data):
self.about_dialog.present() self.about_dialog.present()
@ -940,13 +977,13 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__set_page("donate") self.__set_page("donate")
def _on_liberapay_activate(self, action, data): 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): 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): 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): def _on_add_placeholder_activate(self, action, data):
self.view.add_placeholder() self.view.add_placeholder()
@ -974,18 +1011,18 @@ class CmbWindow(Gtk.ApplicationWindow):
def __on_project_notify(self, obj, pspec): def __on_project_notify(self, obj, pspec):
if self.project: if self.project:
self.turor_waiting_for_user_action = False self.tutor_waiting_for_user_action = False
self.tutor.play() self.tutor.play()
self.disconnect_by_func(self.__on_project_notify) self.disconnect_by_func(self.__on_project_notify)
def __on_object_added(self, project, obj, data): def __on_object_added(self, project, obj, data):
if obj.info.is_a(data): if obj.info.is_a(data):
project.disconnect_by_func(self.__on_object_added) 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() self.tutor.play()
def __on_ui_added(self, project, ui): 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) project.disconnect_by_func(self.__on_ui_added)
self.tutor.play() self.tutor.play()
@ -1001,14 +1038,12 @@ class CmbWindow(Gtk.ApplicationWindow):
self.project.connect("object-added", self.__on_object_added, "GtkGrid") self.project.connect("object-added", self.__on_object_added, "GtkGrid")
elif node == "add-button": elif node == "add-button":
self.project.connect("object-added", self.__on_object_added, "GtkButton") self.project.connect("object-added", self.__on_object_added, "GtkButton")
elif node == "main-menu": elif node in ["menu_button", "main-menu"]:
self.main_menu.props.modal = False self.menu_button.popup()
elif node == "show-type-popover": elif node == "show-type-popover":
widget.props.popover.modal = False
widget.props.popover.popup() widget.props.popover.popup()
elif node == "show-type-popover-gtk": elif node == "show-type-popover-gtk":
child = widget.get_children()[0] child = utils.widget_get_children(widget)[0]
child.props.popover.props.modal = False
child.props.popover.popup() child.props.popover.popup()
def __on_tutor_hide_node(self, tutor, node, widget): def __on_tutor_hide_node(self, tutor, node, widget):
@ -1017,28 +1052,23 @@ class CmbWindow(Gtk.ApplicationWindow):
self.__clear_tutor() self.__clear_tutor()
elif node == "add-project": elif node == "add-project":
if self.__project is None: if self.__project is None:
self.turor_waiting_for_user_action = True self.tutor_waiting_for_user_action = True
self.tutor.pause() self.tutor.pause()
elif node in ["add-ui", "add-window", "add-grid", "add-button"]: 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() self.tutor.pause()
elif node == "main-menu": elif node in ["menu_button", "donate"]:
self.export_all.get_style_context().remove_class("cmb-tutor-highlight") self.menu_button.popdown()
elif node == "donate":
self.main_menu.props.modal = True
self.main_menu.popdown()
elif node == "show-type-popover": elif node == "show-type-popover":
widget.props.popover.modal = True
widget.props.popover.popdown() widget.props.popover.popdown()
elif node == "show-type-popover-gtk": elif node == "show-type-popover-gtk":
child = widget.get_children()[0] child = utils.widget_get_children(widget)[0]
child.props.popover.props.modal = True
child.props.popover.popdown() child.props.popover.popdown()
self.__update_actions() self.__update_actions()
def _on_intro_activate(self, action, data): def _on_intro_activate(self, action, data):
if self.turor_waiting_for_user_action: if self.tutor_waiting_for_user_action:
return return
if self.tutor: if self.tutor:
@ -1058,28 +1088,69 @@ class CmbWindow(Gtk.ApplicationWindow):
self.tutor.connect("hide-node", self.__on_tutor_hide_node) self.tutor.connect("hide-node", self.__on_tutor_hide_node)
self.tutor.play() 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): def __load_window_state(self):
state = self.window_settings.get_uint("state") state = self.window_settings.get_uint("state")
if state & Gdk.WindowState.MAXIMIZED: if state & self.MAXIMIZED:
self.maximize() self.maximize()
elif state & self.FULLSCREEN:
self.fullscreen()
else: else:
size = self.window_settings.get_value("size").unpack() size = self.window_settings.get_value("size").unpack()
self.set_default_size(*size) self.set_default_size(*size)
def __save_window_state(self): 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 if fullscreen:
maximized = state & Gdk.WindowState.MAXIMIZED state = state | self.FULLSCREEN
if maximized:
state = state | self.MAXIMIZED
# Maintain compatibility with Gtk 3 state
self.window_settings.set_uint("state", 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)) 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() self.__save_window_state()
return False return False

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
# #
# CmbContextMenu - Cambalache UI Editor # 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 # This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public # modify it under the terms of the GNU Lesser General Public
@ -23,7 +23,7 @@
import os import os
from gi.repository import GObject, GLib, Gdk, Gtk from gi.repository import GObject, GLib, Gio, Gdk, Gtk
from cambalache import _ from cambalache import _
@ -31,23 +31,17 @@ from cambalache import _
class CmbContextMenu(Gtk.PopoverMenu): class CmbContextMenu(Gtk.PopoverMenu):
__gtype_name__ = "CmbContextMenu" __gtype_name__ = "CmbContextMenu"
gtk_theme = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
target_tk = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE) target_tk = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE)
main_box = Gtk.Template.Child() main_section = Gtk.Template.Child()
separator = Gtk.Template.Child()
css_theme = Gtk.Template.Child()
css_theme_box = Gtk.Template.Child()
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.theme_submenu = None
super().__init__(**kwargs) super().__init__(**kwargs)
self.connect("notify::target-tk", lambda o, p: self.__populate_css_theme_box()) 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): def __populate_css_theme_box(self):
gtk_path = "gtk-3.0" gtk_path = "gtk-3.0"
@ -56,9 +50,17 @@ class CmbContextMenu(Gtk.PopoverMenu):
if self.target_tk == "gtk-4.0": if self.target_tk == "gtk-4.0":
gtk_path = "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(): if self.theme_submenu is None:
self.css_theme_box.remove(child) 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 = [] dirs = []
@ -71,9 +73,6 @@ class CmbContextMenu(Gtk.PopoverMenu):
# Append ~/.themes # Append ~/.themes
dirs.append(os.path.join(GLib.get_home_dir(), ".themes")) dirs.append(os.path.join(GLib.get_home_dir(), ".themes"))
# Default themes
themes = ["Adwaita", "HighContrast", "HighContrastInverse"]
for path in dirs: for path in dirs:
if not os.path.isdir(path): if not os.path.isdir(path):
continue continue
@ -86,23 +85,16 @@ class CmbContextMenu(Gtk.PopoverMenu):
# Dedup and sort # Dedup and sort
themes = list(dict.fromkeys(themes)) 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 group = None
for theme in sorted(themes): for theme in sorted(themes):
button = Gtk.RadioButton(label=theme, group=group, active=self.gtk_theme == theme, visible=True) item = Gio.MenuItem()
if group is None: item.set_label(theme)
group = button item.set_action_and_target_value("win.workspace_theme", GLib.Variant("s", theme))
self.theme_submenu.append_item(item)
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
def popup_at(self, x, y): def popup_at(self, x, y):
r = Gdk.Rectangle() 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.set_pointing_to(r)
self.popup() self.popup()

View File

@ -1,182 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface> <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"> <template class="CmbContextMenu" parent="GtkPopoverMenu">
<property name="can-focus">False</property> <property name="menu-model">menu_model</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>
</template> </template>
</interface> </interface>

View File

@ -23,6 +23,7 @@
from gi.repository import GObject, Gtk from gi.repository import GObject, Gtk
from .cmb_css import CmbCSS from .cmb_css import CmbCSS
from . import utils
@Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_css_editor.ui") @Gtk.Template(resource_path="/ar/xjuan/Cambalache/cmb_css_editor.ui")
@ -121,8 +122,7 @@ class CmbCSSEditor(Gtk.Grid):
def __update_provider_for(self): def __update_provider_for(self):
# Remove all css_ui check buttons # Remove all css_ui check buttons
ui_box_children = self.ui_box.get_children() for child in utils.widget_get_children(self.ui_box):
for child in ui_box_children:
self.ui_box.remove(child) self.ui_box.remove(child)
if self._object is None: 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 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) 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): def __on_file_changed(self, obj):
self.infobar.set_revealed(True) self.infobar.set_revealed(True)

View File

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

View File

@ -1,7 +1,7 @@
# #
# CmbDB - Cambalache DataBase # 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 # This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public # modify it under the terms of the GNU Lesser General Public
@ -28,7 +28,7 @@ import ast
from lxml import etree from lxml import etree
from lxml.builder import E 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 cambalache import config, getLogger, _
from . import cmb_db_migration, utils from . import cmb_db_migration, utils
from .constants import EXTERNAL_TYPE, GMENU_TYPE, GMENU_SECTION_TYPE, GMENU_SUBMENU_TYPE, GMENU_ITEM_TYPE 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})" return f"\t({r})"
def _dump_table(c, table): def _dump_query(c, query):
c.execute(f"SELECT * FROM {table};") c.execute(query)
row = c.fetchone() row = c.fetchone()
if row is None: if row is None:
@ -635,20 +635,27 @@ class CmbDB(GObject.GObject):
return f"\n{retval}\n " return f"\n{retval}\n "
def append_data(project, name, data):
if data is None:
return
element = etree.Element(name)
element.text = data
project.append(element)
self.conn.commit() self.conn.commit()
c = self.conn.cursor() c = self.conn.cursor()
project = E("cambalache-project", version=config.FILE_FORMAT_VERSION, target_tk=self.target_tk) project = E("cambalache-project", version=config.FILE_FORMAT_VERSION, target_tk=self.target_tk)
for table in self.__tables: 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: # DUMP custom properties and signals
continue 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;")
element = etree.Element(table) append_data(project, table, data)
element.text = data
project.append(element)
# Dump xml to file # Dump xml to file
with open(filename, "wb") as fd: with open(filename, "wb") as fd:
@ -1523,12 +1530,11 @@ class CmbDB(GObject.GObject):
if column is not None: if column is not None:
c.execute(f"UPDATE ui SET {column}=? WHERE ui_id=?", (value, ui_id)) c.execute(f"UPDATE ui SET {column}=? WHERE ui_id=?", (value, ui_id))
else: else:
print(child)
custom_fragments.append(child) custom_fragments.append(child)
# self.__unknown_tag(child, None, child.tag)
while Gtk.events_pending(): main_loop = GLib.MainContext.default()
Gtk.main_iteration_do(False) while main_loop.pending():
main_loop.iteration(False)
# Fix object references! # Fix object references!
self.__fix_object_references(ui_id) self.__fix_object_references(ui_id)

View File

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

View File

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

View File

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

View File

@ -29,9 +29,10 @@ from .control import CmbEntry, CmbChildTypeComboBox, cmb_create_editor
from .cmb_property_label import CmbPropertyLabel from .cmb_property_label import CmbPropertyLabel
from cambalache import _ from cambalache import _
from .constants import EXTERNAL_TYPE from .constants import EXTERNAL_TYPE
from . import utils
class CmbObjectEditor(Gtk.ScrolledWindow): class CmbObjectEditor(Gtk.Box):
__gtype_name__ = "CmbObjectEditor" __gtype_name__ = "CmbObjectEditor"
layout = GObject.Property(type=bool, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, default=False) 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) super().__init__(**kwargs)
self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, visible=True) self.props.orientation = Gtk.Orientation.VERTICAL
viewport = Gtk.Viewport(visible=True, shadow_type=Gtk.ShadowType.NONE)
viewport.add(self.box)
self.add(viewport)
def __create_id_editor(self): def __create_id_editor(self):
grid = Gtk.Grid(hexpand=True, row_spacing=4, column_spacing=4) 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) box = Gtk.FlowBox(visible=True, hexpand=True, selection_mode=Gtk.SelectionMode.NONE)
label = Gtk.Label(label=_("Add"), xalign=0, visible=True) label = Gtk.Label(label=_("Add"), xalign=0, visible=True)
box.add(label) box.append(label)
for type_id in info.child_type_shortcuts: for type_id in info.child_type_shortcuts:
button = Gtk.Button(label=type_id, visible=True) button = Gtk.Button(label=type_id, visible=True)
button.connect("clicked", self.__on_shortcut_button_clicked, type_id) button.connect("clicked", self.__on_shortcut_button_clicked, type_id)
box.add(button) box.append(button)
return box return box
@ -127,7 +125,7 @@ class CmbObjectEditor(Gtk.ScrolledWindow):
def __create_child_type_editor(self): def __create_child_type_editor(self):
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) 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) combo = CmbChildTypeComboBox(object=self.__object)
@ -138,12 +136,12 @@ class CmbObjectEditor(Gtk.ScrolledWindow):
"cmb-value", "cmb-value",
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL, GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
) )
box.pack_start(combo, True, True, 0) box.append(combo)
return box return box
def __update_view(self): def __update_view(self):
for child in self.box.get_children(): for child in utils.widget_get_children(self):
self.box.remove(child) self.remove(child)
if self.__object is None: if self.__object is None:
return return
@ -158,10 +156,10 @@ class CmbObjectEditor(Gtk.ScrolledWindow):
# Child Type input # Child Type input
if parent.info.has_child_types(): if parent.info.has_child_types():
self.box.add(self.__create_child_type_editor()) self.append(self.__create_child_type_editor())
else: else:
# ID # ID
self.box.add(self.__create_id_editor()) self.append(self.__create_id_editor())
if obj.type_id == EXTERNAL_TYPE: if obj.type_id == EXTERNAL_TYPE:
label = Gtk.Label( label = Gtk.Label(
@ -174,8 +172,8 @@ It has to be exposed by your application with GtkBuilder expose_object method."
xalign=0, xalign=0,
wrap=True, wrap=True,
) )
self.box.add(label) self.append(label)
self.show_all() self.show()
return return
info = parent.info if self.layout and parent else obj.info 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) expander = Gtk.Expander(label=f"<b>{owner_id}</b>", use_markup=True, expanded=True)
revealer = Gtk.Revealer(reveal_child=True) revealer = Gtk.Revealer(reveal_child=True)
expander.connect("notify::expanded", self.__on_expander_expanded, revealer) expander.connect("notify::expanded", self.__on_expander_expanded, revealer)
revealer.add(grid) revealer.set_child(grid)
self.box.add(expander) self.append(expander)
self.box.add(revealer) self.append(revealer)
self.show_all() self.show()
def __on_object_ui_notify(self, obj, pspec): def __on_object_ui_notify(self, obj, pspec):
if pspec.name == "template-id" and self.__template_switch: 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-added": (GObject.SignalFlags.RUN_FIRST, None, (CmbTypeInfo,)),
"type-info-removed": (GObject.SignalFlags.RUN_FIRST, None, (CmbTypeInfo,)), "type-info-removed": (GObject.SignalFlags.RUN_FIRST, None, (CmbTypeInfo,)),
"type-info-changed": (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) target_tk = GObject.Property(type=str, flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT)
@ -282,9 +281,6 @@ class CmbProject(Gtk.TreeStore):
self.__filename = value self.__filename = value
def save(self): def save(self):
if self.filename is None:
self.filename = self.emit("filename-required")
if self.filename: if self.filename:
self.db.save(self.filename) self.db.save(self.filename)
return True return True
@ -378,6 +374,9 @@ class CmbProject(Gtk.TreeStore):
return (ui, msgs, detail_msg) return (ui, msgs, detail_msg)
def __export(self, ui_id, filename, dirname=None): def __export(self, ui_id, filename, dirname=None):
if filename is None:
return
if not os.path.isabs(filename): if not os.path.isabs(filename):
if dirname is None: if dirname is None:
dirname = os.path.dirname(self.filename) dirname = os.path.dirname(self.filename)
@ -1178,14 +1177,15 @@ class CmbProject(Gtk.TreeStore):
self.__object_update_row(ui.ui_id, template_id) self.__object_update_row(ui.ui_id, template_id)
def _ui_changed(self, ui, field): def _ui_changed(self, ui, field):
iter = self.get_iter_from_object(ui)
if field == "template-id": if field == "template-id":
self.__update_template_type_info(ui) self.__update_template_type_info(ui)
iter = self.get_iter_from_object(ui)
if iter is None:
return
path = self.get_path(iter) path = self.get_path(iter)
self.row_changed(path, iter) self.row_changed(path, iter)
self.emit("ui-changed", ui, field) self.emit("ui-changed", ui, field)
def _ui_library_changed(self, ui, lib): def _ui_library_changed(self, ui, lib):

View File

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

View File

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

View File

@ -40,7 +40,6 @@ class CmbTreeView(Gtk.TreeView):
self.__in_selection_change = False self.__in_selection_change = False
self._selection.connect("changed", self.__on_selection_changed) self._selection.connect("changed", self.__on_selection_changed)
self.set_headers_visible(False) self.set_headers_visible(False)
self.__right_click = False
renderer = Gtk.CellRendererText() renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Object(Type)", renderer) column = Gtk.TreeViewColumn("Object(Type)", renderer)
@ -50,31 +49,14 @@ class CmbTreeView(Gtk.TreeView):
self.connect("notify::model", self.__on_model_notify) self.connect("notify::model", self.__on_model_notify)
self.connect("row-activated", self.__on_row_activated) self.connect("row-activated", self.__on_row_activated)
self.menu = CmbContextMenu(relative_to=self) gesture = Gtk.GestureClick(button=3)
gesture.connect("pressed", self.__on_button_press)
self.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK) self.add_controller(gesture)
self.connect("button-press-event", self.__on_button_press_event)
self.connect("button-release-event", self.__on_button_release_event)
self.set_reorderable(True) self.set_reorderable(True)
def __on_button_press_event(self, widget, event): def __on_button_press(self, widget, npress, x, y):
if event.window != self.get_bin_window() or event.button != 3: retval = self.get_path_at_pos(x, y)
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)
if retval is None: if retval is None:
return False return False
@ -82,7 +64,12 @@ class CmbTreeView(Gtk.TreeView):
path, col, xx, yy = retval path, col, xx, yy = retval
self.get_selection().select_path(path) 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 return True
@ -156,7 +143,7 @@ class CmbTreeView(Gtk.TreeView):
project.set_selection([obj]) project.set_selection([obj])
def do_query_tooltip(self, x, y, keyboard_mode, tooltip): 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: if not retval:
return False return False

View File

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

View File

@ -47,8 +47,9 @@ class CmbTypeChooserPopover(Gtk.Popover):
self._chooser = CmbTypeChooserWidget() self._chooser = CmbTypeChooserWidget()
self._chooser.connect("type-selected", self.__on_type_selected) 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 [ for prop in [
"project", "project",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,87 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface> <interface>
<requires lib="gtk+" version="3.24"/> <requires lib="gtk" version="4.0"/>
<requires lib="webkit2gtk" version="2.28"/> <object class="WebKitSettings" id="settings">
<object class="WebKitSettings" type-func="webkit_settings_get_type" id="settings">
<property name="enable-html5-local-storage">False</property> <property name="enable-html5-local-storage">False</property>
<property name="enable-html5-database">False</property> <property name="enable-html5-database">False</property>
<property name="enable-java">False</property>
<property name="enable-fullscreen">False</property> <property name="enable-fullscreen">False</property>
<property name="enable-webaudio">False</property> <property name="enable-webaudio">False</property>
<property name="media-playback-allows-inline">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="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> <property name="enable-media">False</property>
</object> </object>
<template class="CmbView" parent="GtkStack"> <template class="CmbView" parent="GtkBox">
<property name="visible">True</property> <child>
<property name="can-focus">False</property> <object class="GtkStack" id="stack">
<property name="events">GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property> <property name="hexpand">true</property>
<property name="transition-duration">300</property> <property name="transition-duration">300</property>
<property name="transition-type">crossfade</property> <property name="transition-type">crossfade</property>
<child> <child>
<object class="WebKitWebView" type-func="webkit_web_view_get_type" id="webview"> <object class="GtkStackPage">
<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>
<property name="name">ui_view</property> <property name="name">ui_view</property>
<property name="title" translatable="yes">Project View</property> <property name="title" translatable="1">Project View</property>
</packing> <property name="child">
</child> <object class="WebKitWebView" id="webview">
<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="visible">True</property> <property name="visible">True</property>
<property name="can-focus">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> <child>
<object class="CmbSourceView" id="text_view"> <placeholder/>
<property name="visible">True</property> </child>
<property name="can-focus">True</property> </object>
<property name="editable">False</property> </property>
<property name="cursor-visible">False</property> </object>
<property name="lang">xml</property> </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> </object>
</child> </child>
</object> </object>
<packing> </property>
<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>
</object> </object>
<packing>
<property name="name">ui_xml</property>
<property name="title" translatable="yes">UI Definition</property>
<property name="position">1</property>
</packing>
</child> </child>
</object>
</child>
</template> </template>
</interface> </interface>

View File

@ -35,23 +35,30 @@ class CmbColorEntry(Gtk.Box):
super().__init__(**kwargs) super().__init__(**kwargs)
self.entry = Gtk.Entry(visible=True, width_chars=14, editable=False) 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.__default_rgba = self.button.props.rgba
self.pack_start(self.entry, False, True, 0) self.append(self.entry)
self.pack_start(self.button, False, True, 4) 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) self.entry.connect("icon-press", self.__on_entry_icon_pressed)
def __on_entry_icon_pressed(self, widget, icon_pos, event): def __on_entry_icon_pressed(self, widget, icon_pos, event):
self.cmb_value = None 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: 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: else:
self.cmb_value = self.button.props.rgba.to_string() if self.button.props.rgba else None 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: if value:
valid, color = Gdk.Color.parse(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: else:
rgba = Gdk.RGBA() rgba = Gdk.RGBA()

View File

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

View File

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

View File

@ -24,7 +24,7 @@
import os import os
from cambalache import _ 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 .cmb_entry import CmbEntry
from .icon_naming_spec import standard_icon_context, standard_icon_names 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 # Model, store it in a Python class variable to share between all instances
icon_model = None icon_model = None
iconlist = []
def __init__(self, **kwargs): def __init__(self, **kwargs):
self._filters = {} self._filters = {}
@ -91,32 +93,56 @@ class CmbIconNameEntry(CmbEntry):
iconlist = [] iconlist = []
theme = Gtk.IconTheme.get_default() theme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default())
for context in theme.list_contexts(): # FIXME: get the context/category of each icon
for icon in theme.list_icons(context): for icon in theme.get_icon_names():
iconlist.append((icon, context, icon in standard_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()): for icon, context, standard in sorted(iconlist, key=lambda i: i[0].lower()):
if icon.endswith(".symbolic"): if icon.endswith(".symbolic"):
continue continue
info = theme.lookup_icon(icon, 32, Gtk.IconLookupFlags.FORCE_SIZE) icon_paintable = theme.lookup_icon(icon, None, 32, 1, Gtk.TextDirection.NONE, Gtk.IconLookupFlags.PRELOAD)
symbolic = info.is_symbolic() 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 continue
standard_symbolic = symbolic and icon.removesuffix("-symbolic") in standard_icon_names standard_symbolic = symbolic and icon.removesuffix("-symbolic") in standard_icon_names
iter = cls.icon_model.append( try:
[icon, icon if standard else f"<i>{icon}</i>", context, standard, symbolic, standard_symbolic, None] 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) )
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 @classmethod
def __load_icon_finish(cls, info, res, data): def __load_file_finish(cls, obj, res, iter):
cls.icon_model[data][6] = info.load_icon_finish(res) 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): def __model_filter_func(self, model, iter, data):
if self.standard_only and self.symbolic_only: if self.standard_only and self.symbolic_only:
@ -146,20 +172,23 @@ class CmbIconNameEntry(CmbEntry):
else: else:
self.cmb_value = None 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 # Create popover with icon chooser
popover = Gtk.Popover(relative_to=self) popover = Gtk.Popover()
popover.set_parent(self)
hbox = Gtk.Box(visible=True) hbox = Gtk.Box(visible=True)
vbox = Gtk.Box(visible=True, orientation=Gtk.Orientation.VERTICAL, vexpand=True) vbox = Gtk.Box(visible=True, orientation=Gtk.Orientation.VERTICAL, vexpand=True)
stack = Gtk.Stack(visible=True, transition_type=Gtk.StackTransitionType.CROSSFADE) stack = Gtk.Stack(visible=True, transition_type=Gtk.StackTransitionType.CROSSFADE)
sidebar = Gtk.StackSidebar(visible=True, stack=stack, vexpand=True) sidebar = Gtk.StackSidebar(visible=True, stack=stack, vexpand=True)
vbox.pack_start(sidebar, True, True, 4) vbox.append(sidebar)
hbox.pack_start(vbox, False, True, 4) hbox.append(vbox)
hbox.pack_start(stack, True, True, 4) 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") sorted_contexts.insert(0, "cmb_all")
# Add one icon view per context # 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) 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 = 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) 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)) stack.add_titled(sw, context, standard_icon_context.get(context, context))
# Add filters # Add filters
@ -185,8 +214,8 @@ class CmbIconNameEntry(CmbEntry):
self, prop, check, "active", GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL self, prop, check, "active", GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL
) )
check.connect_after("notify::active", self.__on_check_active_notify) 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_css_class("cmb-icon-chooser")
popover.add(hbox) popover.set_child(hbox)
popover.popup() 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) parent.project.add_object(parent.ui_id, info.type_id, parent_id=parent.object_id, inline_property=self.prop.property_id)
self.__update_icons() self.__update_icons()
def __on_icon_pressed(self, widget, icon_pos, event): def __on_icon_pressed(self, widget, icon_pos):
parent = self.parent parent = self.parent
project = parent.project project = parent.project
prop = self.prop prop = self.prop
@ -116,7 +116,8 @@ class CmbObjectChooser(Gtk.Entry):
project.remove_object(obj) project.remove_object(obj)
self.__update_icons() self.__update_icons()
else: 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.project = project
chooser.connect("type-selected", self.__on_type_selected) chooser.connect("type-selected", self.__on_type_selected)
chooser.popup() chooser.popup()

View File

@ -33,7 +33,6 @@ class CmbTextView(Gtk.ScrolledWindow):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.props.shadow_type = Gtk.ShadowType.IN
self.props.height_request = 64 self.props.height_request = 64
self.buffer = CmbTextBuffer() self.buffer = CmbTextBuffer()
self.view = Gtk.TextView(visible=True, buffer=self.buffer) self.view = Gtk.TextView(visible=True, buffer=self.buffer)
@ -46,4 +45,4 @@ class CmbTextView(Gtk.ScrolledWindow):
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL, 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) super().__init__(**kwargs)
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
box.pack_start(Gtk.Label(label=_("<b>Translation</b>"), use_markup=True), False, True, 4) box.append(Gtk.Label(label=_("<b>Translation</b>"), use_markup=True))
box.pack_start(Gtk.Separator(), False, False, 0) box.append(Gtk.Separator())
self._translation = CmbTranslatableWidget() self._translation = CmbTranslatableWidget()
box.pack_start(self._translation, False, False, 0) box.append(self._translation)
box.show_all() self.set_child(box)
self.add(box)
def bind_properties(self, target): def bind_properties(self, target):
self._translation.bind_properties(target) self._translation.bind_properties(target)

View File

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

View File

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

View File

@ -100,15 +100,20 @@ class MrgGtkWidget(MrgController):
if self.object is None: if self.object is None:
return 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 # Update toplevel backdrop state
if Gtk.MAJOR_VERSION == 4: 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() toplevel = self.object.get_root()
else: 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() toplevel = self.object.get_toplevel()
if toplevel: if toplevel:

View File

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

View File

@ -48,15 +48,17 @@ def version_cmp_str(a, b):
def unset_scroll_event(widget): def unset_scroll_event(widget):
def ignore_scroll_event(widget, event): pass
Gtk.propagate_event(widget.get_parent(), event) # FIXME: GTK4
return True # def ignore_scroll_event(widget, event):
# Gtk.propagate_event(widget.get_parent(), event)
# return True
events = widget.get_events() # events = widget.get_events()
widget.set_events(events & ~(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK)) # widget.set_events(events & ~(Gdk.EventMask.SCROLL_MASK | Gdk.EventMask.SMOOTH_SCROLL_MASK))
if isinstance(widget, Gtk.ComboBox): # if isinstance(widget, Gtk.ComboBox):
widget.connect("scroll-event", ignore_scroll_event) # widget.connect("scroll-event", ignore_scroll_event)
def get_version_warning(target, version, deprecated_version, this): 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 f"UI targets {target} but {this} was deprecated in {deprecated_version}"
return None 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 # File format version follows app version and only changes when there is a
# change that prevents older versions to load it. # change that prevents older versions to load it.
fileformatversion = '0.13.1' fileformatversion = '0.17.0'
python = import('python') python = import('python')
python_bin = python.find_installation('python3') python_bin = python.find_installation('python3')

View File

@ -61,7 +61,7 @@ def close_compositor():
# Make sure the right gtk version is loaded # 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 # Make sure we can run Cambalache from sources
cmb_init_dev() cmb_init_dev()

View File

@ -28,28 +28,23 @@ import struct
from gi.repository import GLib, Gtk 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 wait_for_drawing(window):
def on_window_draw(widget, cr, loop): done = { "done": False }
loop.quit() main_loop = GLib.MainContext.default()
return False
def quit_when_idle(loop): def quit_main_loop_callback(widget, frame_clock, done):
loop.quit() done["done"] = True
print("BBBBBBBBB")
main_loop.wakeup()
print("CCCCCCCCCCCCCC")
return GLib.SOURCE_REMOVE 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. print("AAAAAAAAAAAA")
# We are running in a dedicated compositor so the window should not be obstructed by other windows while not done["done"]:
window.connect("draw", on_window_draw, loop) main_loop.iteration(True)
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()
def surface_write_ppm(surface, path): def surface_write_ppm(surface, path):
@ -71,13 +66,20 @@ def window_screenshot(window):
# Wait for window to finish drawing # Wait for window to finish drawing
wait_for_drawing(window) wait_for_drawing(window)
w = window.get_allocated_width() paintable = Gtk.WidgetPaintable.new(window)
h = window.get_allocated_height() snapshot = Gtk.Snapshot()
w = paintable.get_intrinsic_width()
h = paintable.get_intrinsic_height()
# Draw widget to cairo surface # Draw widget to cairo surface
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h) surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
paintable.snapshot(snapshot, w, h)
node = snapshot.to_node()
cr = cairo.Context(surface) cr = cairo.Context(surface)
window.draw(cr) node.draw(cr)
surface.flush() surface.flush()
@ -124,26 +126,36 @@ def mean_squared_error(original, screenshot, ignore_color=None):
def process_all_pending_gtk_events(): def process_all_pending_gtk_events():
while Gtk.events_pending(): main_loop = GLib.MainContext.default()
Gtk.main_iteration_do(False) 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): def find_by_buildable_id(widget, name):
retval = None 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 return widget
if not isinstance(widget, Gtk.Container): for child in __get_children(widget):
return None retval = find_by_buildable_id(child, name)
if retval:
return retval
for child in widget.get_children(): if isinstance(child, Gtk.Buildable) and Gtk.Buildable.get_buildable_id(child) == name:
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:
return child return child
return retval return retval
@ -159,8 +171,9 @@ def cmb_create_app():
window = None window = None
# Spin until we get the main window # Spin until we get the main window
while Gtk.events_pending() and not window: main_loop = GLib.MainContext.default()
Gtk.main_iteration_do(False) while main_loop.pending() and not window:
main_loop.iteration(False)
# Get window if any # Get window if any
windows = app.get_windows() windows = app.get_windows()