예제 #1
0
    def __init__(self, app):
        View.__init__(self, app, sub_title='Tweak how files are synchronized')

        self._grid = Gtk.Grid()
        self._grid.set_margin_start(30)
        self._grid.set_margin_end(40)
        self._grid.set_margin_top(5)
        self._grid.set_margin_bottom(15)
        self._grid.set_hexpand(True)
        self._grid.set_vexpand(True)
        self._grid.set_halign(Gtk.Align.FILL)
        self._grid.set_valign(Gtk.Align.FILL)
        self.add(self._grid)

        self.save_settings = False
        self.sections = {}
        self.metadata = {}

        self.appy_btn = SuggestedButton()
        self.deny_btn = DestructiveButton('Reset to defaults')

        self.appy_btn.connect('clicked', self.on_apply_settings)
        self.deny_btn.connect('clicked', self.on_reset_to_defaults)
        self.search_entry.connect('search-changed', self.on_search_changed)

        # Initialize from current settings:
        self.build()
예제 #2
0
    def __init__(self, app):
        View.__init__(
            self, app, sub_title='Configure how duplicates are searched'
        )

        self._grid = Gtk.Grid()
        self._grid.set_margin_start(30)
        self._grid.set_margin_end(40)
        self._grid.set_margin_top(5)
        self._grid.set_margin_bottom(15)
        self._grid.set_hexpand(True)
        self._grid.set_vexpand(True)
        self._grid.set_halign(Gtk.Align.FILL)
        self._grid.set_valign(Gtk.Align.FILL)
        self.add(self._grid)

        self.save_settings = False
        self.sections = {}
        self.metadata = {}

        self.appy_btn = SuggestedButton()
        self.deny_btn = DestructiveButton('Reset to defaults')

        self.appy_btn.connect('clicked', self.on_apply_settings)
        self.deny_btn.connect('clicked', self.on_reset_to_defaults)
        self.search_entry.connect(
            'search-changed', self.on_search_changed
        )

        # Initialize from current settings:
        self.build()
예제 #3
0
class SettingsView(View):
    """Generic GSettingsView in a modern Gnome like appearance."""
    def __init__(self, app):
        View.__init__(
            self, app, sub_title='Configure how duplicates are searched'
        )

        self._grid = Gtk.Grid()
        self._grid.set_margin_start(30)
        self._grid.set_margin_end(40)
        self._grid.set_margin_top(5)
        self._grid.set_margin_bottom(15)
        self._grid.set_hexpand(True)
        self._grid.set_vexpand(True)
        self._grid.set_halign(Gtk.Align.FILL)
        self._grid.set_valign(Gtk.Align.FILL)
        self.add(self._grid)

        self.save_settings = False
        self.sections = {}
        self.metadata = {}

        self.appy_btn = SuggestedButton()
        self.deny_btn = DestructiveButton('Reset to defaults')

        self.appy_btn.connect('clicked', self.on_apply_settings)
        self.deny_btn.connect('clicked', self.on_reset_to_defaults)
        self.search_entry.connect(
            'search-changed', self.on_search_changed
        )

        # Initialize from current settings:
        self.build()

    def append_section(self, heading):
        """Append a new named section for multiple entries with `heading`"""
        box = Gtk.ListBox()
        box.set_selection_mode(Gtk.SelectionMode.NONE)
        box.set_size_request(350, -1)
        box.set_hexpand(True)

        frame = Gtk.Frame()
        frame.set_halign(Gtk.Align.FILL)
        frame.add(box)

        label = Gtk.Label()
        label.set_margin_top(30)
        label.set_markup(
            '<b>{}:</b>'.format(GLib.markup_escape_text(heading, -1))
        )
        label.set_halign(Gtk.Align.START)
        label.set_margin_bottom(2)

        self.sections[heading.lower()] = box
        self.metadata[heading.lower()] = {
            'label': label, 'frame': frame
        }
        self._grid.attach(label, 0, len(self._grid), 1, 1)
        self._grid.attach(frame, 0, len(self._grid), 1, 1)

    def append_entry(self, section, val_widget, key_name, summary, desc=None):
        """Append an entry to a named section.

        section: A previously inserted section name.
        val_widget: The widget to show the key in.
        summary: A short summary to show.
        desc: A longer description.
        """
        desc_label = Gtk.Label(desc or '')
        summ_label = Gtk.Label(summary or '')

        desc_label.get_style_context().add_class(
            Gtk.STYLE_CLASS_DIM_LABEL
        )

        for label in desc_label, summ_label:
            label.set_use_markup(True)
            label.set_halign(Gtk.Align.FILL)
            label.set_hexpand(True)
            label.set_halign(Gtk.Align.START)

        val_widget.set_halign(Gtk.Align.END)

        sub_grid = Gtk.Grid()
        sub_grid.attach(summ_label, 0, 0, 1, 1)
        sub_grid.attach(desc_label, 0, 1, 1, 1)
        sub_grid.attach(val_widget, 1, 0, 1, 2)
        sub_grid.set_border_width(3)

        listbox = self.sections[section.lower()]
        if len(listbox):
            sep_row = Gtk.ListBoxRow()
            sep_row.set_activatable(False)
            sep_row.add(Gtk.Separator())
            listbox.insert(sep_row, -1)

        row = Gtk.ListBoxRow()
        row.add(sub_grid)
        row.set_can_focus(False)
        listbox.insert(row, -1)

        self.metadata[section.lower()][key_name] = {
            'summary': summary.lower() or '',
            'description': desc.lower() or '',
            'widget': row
        }

    def reset_to_defaults(self):
        """Reset whole view and keys to their defaults"""
        for key_name in self.app.settings.list_keys():
            self.app.settings.reset(key_name)

    def build(self):
        """Built all entries and sections"""
        gst = self.app.settings
        entry_rows = []

        for key_name in gst.list_keys():
            key = gst.get_property('settings-schema').get_key(key_name)
            variant_key = gst.get_value(key_name)

            # Try to find a way to render this option:
            constructor = VARIANT_TO_WIDGET.get(variant_key.get_type_string())
            if constructor is None:
                continue

            # Get the key summary and description:
            summary = '{}'.format(key.get_summary())

            # This is an extension of this code:
            if summary.startswith('[hidden]'):
                continue

            order, order_grep = 0, re.match(r'\[(\d+)]\s(.*)', summary)
            if order_grep is not None:
                order, summary = int(order_grep.group(1)), order_grep.group(2)

            description = key.get_description()
            if description:
                description = '<small>{desc}</small>'.format(
                    desc=key.get_description()
                )

            # Get a fitting, readily prepared configure widget
            val_widget = constructor(gst, key_name, summary, description)

            # Try to find the section name by the keyname.
            # This is an extension made by this code and not part of GSettings.
            section = ''
            if '-' in key_name:
                section, _ = key_name.split('-', maxsplit=1)

            entry_rows.append(
                (order, section, val_widget, key_name, summary, description)
            )

        for section in sorted(set([entry[1] for entry in entry_rows])):
            self.append_section(section.capitalize())

        for entry in sorted(entry_rows, key=itemgetter(0, 2)):
            self.append_entry(*entry[1:])

    ####################
    # SIGNAL CALLBACKS #
    ####################

    def on_search_changed(self, _):
        """Called once the user enteres a new search query."""
        query = self.search_entry.get_text().lower()

        def _set_vis(widget, state, lower):
            """Set opacity and sensitivity in one."""
            widget.set_sensitive(state)
            widget.set_opacity(1.0 if state else lower)

        for _, metadata in self.metadata.items():
            section_visible = 0

            for key_name, info in metadata.items():
                if key_name in ['label', 'frame']:
                    continue

                row = info['widget']
                if query in info['summary'] or query in info['description']:
                    section_visible += 1
                    _set_vis(row, True, 0.5)
                else:
                    _set_vis(row, False, 0.5)

            section_frame = metadata['frame']
            section_label = metadata['label']
            _set_vis(section_frame, section_visible > 0, 0.2)
            _set_vis(section_label, section_visible > 0, 0.2)

    def on_view_enter(self):
        """Called once the view is visible. Delay save of settings."""
        self.save_settings = False
        self.app.settings.delay()

        # Give the buttons a specific context meaning:
        self.on_key_changed(self.app.settings, None)
        self.app.settings.connect('changed', self.on_key_changed)

        self.add_header_widget(self.appy_btn)
        self.add_header_widget(self.deny_btn, Gtk.Align.START)

        # It's usually more useful when switching back to the latest view,
        # not to the view to the right since user might just have wanted
        # to have a quick settings change from anywhere in the application.
        self.app_window.views.switch_to_previous_next()

    def on_view_leave(self):
        """Called once the view gets out of sight. Revert or apply."""
        if self.save_settings:
            self.app.settings.apply()
        else:
            self.app.settings.revert()

        self.clear_header_widgets()

    def on_apply_settings(self, *_):
        """Callback for the apply button."""
        self.save_settings = True
        self.app_window.views.switch_to_previous()

    def on_reset_to_defaults(self, *_):
        """Callback for the reset button."""
        self.app.settings.revert()

        GLib.timeout_add(
            100, lambda: self.reset_to_defaults() or self.app.settings.delay()
        )

        self.save_settings = False
        self.app_window.views.switch_to_previous()

    def on_key_changed(self, settings, _):
        """Called when a key in GSettings changes."""
        is_sensitive = settings.get_has_unapplied()
        self.appy_btn.set_sensitive(is_sensitive)
        self.deny_btn.set_sensitive(is_sensitive)

    def on_default_action(self):
        """Called on Ctrl-Enter"""
        if self.appy_btn.is_sensitive():
            self.on_apply_settings()
        else:
            self.app_window.views.switch_to_previous()
예제 #4
0
class SettingsView(View):
    """Generic GSettingsView in a modern Gnome like appearance."""
    def __init__(self, app):
        View.__init__(self, app, sub_title='Tweak how files are synchronized')

        self._grid = Gtk.Grid()
        self._grid.set_margin_start(30)
        self._grid.set_margin_end(40)
        self._grid.set_margin_top(5)
        self._grid.set_margin_bottom(15)
        self._grid.set_hexpand(True)
        self._grid.set_vexpand(True)
        self._grid.set_halign(Gtk.Align.FILL)
        self._grid.set_valign(Gtk.Align.FILL)
        self.add(self._grid)

        self.save_settings = False
        self.sections = {}
        self.metadata = {}

        self.appy_btn = SuggestedButton()
        self.deny_btn = DestructiveButton('Reset to defaults')

        self.appy_btn.connect('clicked', self.on_apply_settings)
        self.deny_btn.connect('clicked', self.on_reset_to_defaults)
        self.search_entry.connect('search-changed', self.on_search_changed)

        # Initialize from current settings:
        self.build()

    def append_section(self, heading):
        """Append a new named section for multiple entries with `heading`"""
        box = Gtk.ListBox()
        box.set_selection_mode(Gtk.SelectionMode.NONE)
        box.set_size_request(350, -1)
        box.set_hexpand(True)

        frame = Gtk.Frame()
        frame.set_halign(Gtk.Align.FILL)
        frame.add(box)

        label = Gtk.Label()
        label.set_margin_top(30)
        label.set_markup('<b>{}:</b>'.format(
            GLib.markup_escape_text(heading, -1)))
        label.set_halign(Gtk.Align.START)
        label.set_margin_bottom(2)

        self.sections[heading.lower()] = box
        self.metadata[heading.lower()] = {'label': label, 'frame': frame}
        self._grid.attach(label, 0, len(self._grid), 1, 1)
        self._grid.attach(frame, 0, len(self._grid), 1, 1)

    def append_entry(self, section, val_widget, key_name, summary, desc=None):
        """Append an entry to a named section.

        section: A previously inserted section name.
        val_widget: The widget to show the key in.
        summary: A short summary to show.
        desc: A longer description.
        """
        desc_label = Gtk.Label(desc or '')
        summ_label = Gtk.Label(summary or '')

        desc_label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL)

        for label in desc_label, summ_label:
            label.set_use_markup(True)
            label.set_halign(Gtk.Align.FILL)
            label.set_hexpand(True)
            label.set_halign(Gtk.Align.START)

        val_widget.set_halign(Gtk.Align.END)

        sub_grid = Gtk.Grid()
        sub_grid.attach(summ_label, 0, 0, 1, 1)
        sub_grid.attach(desc_label, 0, 1, 1, 1)
        sub_grid.attach(val_widget, 1, 0, 1, 2)
        sub_grid.set_border_width(3)

        listbox = self.sections[section.lower()]
        if len(listbox):
            sep_row = Gtk.ListBoxRow()

            # Needs gtk >= 3.14
            if hasattr(sep_row, 'set_activatable'):
                sep_row.set_activatable(False)

            sep_row.add(Gtk.Separator())
            listbox.insert(sep_row, -1)

        row = Gtk.ListBoxRow()
        row.add(sub_grid)
        row.set_can_focus(False)
        listbox.insert(row, -1)

        self.metadata[section.lower()][key_name] = {
            'summary': summary.lower() or '',
            'description': desc.lower() or '',
            'widget': row
        }

    def reset_to_defaults(self):
        """Reset whole view and keys to their defaults"""
        for key_name in self.app.settings.list_keys():
            self.app.settings.reset(key_name)

    def build(self):
        """Built all entries and sections"""
        gst = self.app.settings
        entry_rows = []

        for key_name in gst.list_keys():
            key = gst.get_property('settings-schema').get_key(key_name)
            variant_key = gst.get_value(key_name)

            # Try to find a way to render this option:
            constructor = VARIANT_TO_WIDGET.get(variant_key.get_type_string())
            if constructor is None:
                continue

            # Get the key summary and description:
            summary = '{}'.format(key.get_summary())

            # This is an extension of this code:
            if summary.startswith('[hidden]'):
                continue

            order, order_grep = 0, re.match(r'\[(\d+)]\s(.*)', summary)
            if order_grep is not None:
                order, summary = int(order_grep.group(1)), order_grep.group(2)

            description = key.get_description()
            if description:
                description = '<small>{desc}</small>'.format(
                    desc=key.get_description())

            # Get a fitting, readily prepared configure widget
            val_widget = constructor(gst, key_name, summary, description)

            # Try to find the section name by the keyname.
            # This is an extension made by this code and not part of GSettings.
            section = ''
            if '-' in key_name:
                section, _ = key_name.split('-', maxsplit=1)

            entry_rows.append(
                (order, section, val_widget, key_name, summary, description))

        for section in sorted(set([entry[1] for entry in entry_rows])):
            self.append_section(section.capitalize())

        for entry in sorted(entry_rows, key=itemgetter(0, 2)):
            self.append_entry(*entry[1:])

    ####################
    # SIGNAL CALLBACKS #
    ####################

    def on_search_changed(self, _):
        """Called once the user enteres a new search query."""
        query = self.search_entry.get_text().lower()

        def _set_vis(widget, state, lower):
            """Set opacity and sensitivity in one."""
            widget.set_sensitive(state)
            widget.set_opacity(1.0 if state else lower)

        for _, metadata in self.metadata.items():
            section_visible = 0

            for key_name, info in metadata.items():
                if key_name in ['label', 'frame']:
                    continue

                row = info['widget']
                if query in info['summary'] or query in info['description']:
                    section_visible += 1
                    _set_vis(row, True, 0.5)
                else:
                    _set_vis(row, False, 0.5)

            section_frame = metadata['frame']
            section_label = metadata['label']
            _set_vis(section_frame, section_visible > 0, 0.2)
            _set_vis(section_label, section_visible > 0, 0.2)

    def on_view_enter(self):
        """Called once the view is visible. Delay save of settings."""
        self.save_settings = False
        self.app.settings.delay()

        # Give the buttons a specific context meaning:
        self.on_key_changed(self.app.settings, None)
        self.app.settings.connect('changed', self.on_key_changed)

        self.add_header_widget(self.appy_btn)
        self.add_header_widget(self.deny_btn, Gtk.Align.START)

        # It's usually more useful when switching back to the latest view,
        # not to the view to the right since user might just have wanted
        # to have a quick settings change from anywhere in the application.
        self.app_window.views.switch_to_previous_next()

    def on_view_leave(self):
        """Called once the view gets out of sight. Revert or apply."""
        if self.save_settings:
            self.app.settings.apply()
        else:
            self.app.settings.revert()

        self.clear_header_widgets()

    def on_apply_settings(self, *_):
        """Callback for the apply button."""
        self.save_settings = True
        self.app_window.views.switch_to_previous()

    def on_reset_to_defaults(self, *_):
        """Callback for the reset button."""
        self.app.settings.revert()

        GLib.timeout_add(
            100, lambda: self.reset_to_defaults() or self.app.settings.delay())

        self.save_settings = False
        self.app_window.views.switch_to_previous()

    def on_key_changed(self, settings, _):
        """Called when a key in GSettings changes."""
        is_sensitive = settings.get_has_unapplied()
        self.appy_btn.set_sensitive(is_sensitive)
        self.deny_btn.set_sensitive(is_sensitive)

    def on_default_action(self):
        """Called on Ctrl-Enter"""
        if self.appy_btn.is_sensitive():
            self.on_apply_settings()
        else:
            self.app_window.views.switch_to_previous()