Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • blackfennec/extensions/base
1 result
Select Git revision
Show changes
Commits on Source (11)
Showing
with 281 additions and 41 deletions
from blackfennec.extension.extension import Extension
from blackfennec.extension.extension_api import ExtensionApi
from blackfennec.extension_system.extension import Extension
from blackfennec.extension_system.extension_api import ExtensionApi
from base.extension import BaseExtension
import gi
......@@ -7,5 +7,6 @@ import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
def create(api: ExtensionApi) -> Extension:
return BaseExtension(api)
......@@ -6,6 +6,8 @@ from gi.repository import Gtk, Adw
from base.date_time.date_time_editor import DateTimeEditor
from base.date_time.date_time_view_model import DateTimeViewModel
from blackfennec.presentation_system.type_view import TypeView
logger = logging.getLogger(__name__)
BASE_DIR = Path(__file__).resolve().parent
......@@ -13,7 +15,7 @@ UI_TEMPLATE = str(BASE_DIR.joinpath('date_time_preview.ui'))
@Gtk.Template(filename=UI_TEMPLATE)
class DateTimePreview(Gtk.MenuButton):
class DateTimePreview(Gtk.MenuButton, TypeView):
"""View for the core type DateTime."""
__gtype_name__ = 'DateTimePreview'
......@@ -25,7 +27,8 @@ class DateTimePreview(Gtk.MenuButton):
Args:
view_model (DateTimeViewModel): The view_model.
"""
super().__init__()
Gtk.MenuButton.__init__(self)
TypeView.__init__(self)
self._view_model = view_model
self._view_model.bind(changed=self._set_date_time)
self._set_date_time(self, self._view_model.date_time)
......
# -*- coding: utf-8 -*-
from base.date_time.date_time_view_model import DateTimeViewModel
from blackfennec.interpretation.interpretation import Interpretation
from blackfennec.interpretation.specification import Specification
from blackfennec.type_system.interpretation.interpretation import Interpretation
from blackfennec.type_system.interpretation.specification import Specification
from base.date_time.date_time_preview import DateTimePreview
from blackfennec.presentation_system.type_view_factory import TypeViewFactory
class DateTimeViewFactory:
class DateTimeViewFactory(TypeViewFactory):
"""Creator of the DateTimePreview"""
def satisfies(self, specification: Specification) -> bool:
......
......@@ -6,7 +6,7 @@ from blackfennec.util.change_notification_dispatch_mixin import \
ChangeNotificationDispatchMixin
from blackfennec.util.observable import Observable
from base.date_time.date_time import DateTime
from blackfennec.interpretation.interpretation import Interpretation
from blackfennec.type_system.interpretation.interpretation import Interpretation
logger = logging.getLogger(__name__)
......
from pathlib import Path
from blackfennec.extension.extension import Extension
from blackfennec.extension.extension_api import ExtensionApi
from blackfennec.interpretation.specification import Specification
from base.url.url_view_factory import UrlViewFactory
from blackfennec.extension_system.extension import Extension
from blackfennec.extension_system.extension_api import ExtensionApi
from blackfennec.type_system.interpretation.specification import Specification
from base.date_time.date_time import DateTime
from base.date_time.date_time_view_factory import DateTimeViewFactory
from base.file.file_view_factory import FileViewFactory
from base.file.actions.guess_mime_type import \
GuessMimeTypeAction
from base.image.image_view_factory import ImageViewFactory
BASE_NAME = Path(__file__).parent.as_posix()
class BaseExtension(Extension):
def __init__(self, api: ExtensionApi):
super().__init__('base', api, dependencies={'core'})
self.types = []
self.types = {}
self.view_factories = {
'DateTime': DateTimeViewFactory(),
'File': FileViewFactory(self._api.document_registry),
'Image': ImageViewFactory(self._api.document_registry),
'Url': UrlViewFactory(self._api.ui_service),
}
self.view_factories = [
DateTimeViewFactory(),
FileViewFactory(self._api.document_registry),
ImageViewFactory(self._api.document_registry),
]
self.actions = []
def register_types(self):
self.types = [
DateTime.TYPE,
self._api.type_loader.load(BASE_NAME + '/file/file.json'),
self._api.type_loader.load(BASE_NAME + '/image/image.json')
]
self.types = {
'DateTime': DateTime.TYPE,
'File': self._api.type_loader.load(BASE_NAME + '/file/file.json'),
'Image': self._api.type_loader.load(BASE_NAME + '/image/image.json'),
'Url': self._api.type_loader.load(BASE_NAME + '/url/url.json'),
}
self._api.type_registry.register_type(DateTime.TYPE)
def deregister_types(self):
for t in self.types:
for t in self.types.values():
self._api.type_registry.deregister_type(t)
def register_actions(self):
file_type = self.types['File']
guess_mime_type_action = GuessMimeTypeAction(
file_type,
self._api.resource_type_registry
)
self._api.action_registry.register_action(guess_mime_type_action)
self.actions.append(guess_mime_type_action)
def deregister_actions(self):
for action in self.actions:
self._api.action_registry.register_action(action)
def register_view_factories(self):
for specification in [Specification(), Specification(True)]:
for type, view_factory in zip(self.types, self.view_factories):
for type_name, view_factory in self.view_factories.items():
type = self.types[type_name]
if not view_factory.satisfies(specification):
continue
self._api.view_factory_registry.register_view_factory(
self._api.view_factory_registry.register_type_view_factory(
type, specification, view_factory)
def deregister_view_factories(self):
for specification in [Specification(), Specification(True)]:
for type, view_factory in zip(self.types, self.view_factories):
for type_name, view_factory in self.view_factories.items():
type = self.types[type_name]
if not view_factory.satisfies(specification):
continue
self._api.view_factory_registry.deregister_view_factory(
self._api.view_factory_registry.deregister_type_view_factory(
type, specification)
from blackfennec.action_system.action import Action
from blackfennec.action_system.context import Context
from ..file import File
from blackfennec.document_system.mime_type.mime_type import MimeType
from blackfennec.document_system.resource_type.resource_type import ResourceType
from blackfennec.document_system.resource_type.resource_type_registry import \
ResourceTypeRegistry
class GuessMimeTypeAction(Action):
def __init__(self, map_type, resource_type_registry: ResourceTypeRegistry):
super().__init__(map_type)
self._resource_type_registry: ResourceTypeRegistry = \
resource_type_registry
def execute(self, context: Context):
file_type = File(context.structure)
resource_type_id = ResourceType.try_determine_resource_type(file_type.file_path)
resource_type = self._resource_type_registry.resource_types[
resource_type_id
]
mime_type = MimeType.try_determine_mime_type(
file_type.file_path, resource_type)
file_type.file_type = mime_type
@property
def name(self):
return "guess mime type"
@property
def description(self):
return """Tries to determine the mime type of a file."""
......@@ -6,6 +6,7 @@ from gi.repository import Gtk, Adw
from base.file.file_view_model import FileViewModel
from blackfennec.presentation_system.type_view import TypeView
from blackfennec.util.change_notification import ChangeNotification
logger = logging.getLogger(__name__)
......@@ -15,7 +16,7 @@ UI_TEMPLATE = str(BASE_DIR.joinpath('file_preview.ui'))
@Gtk.Template(filename=UI_TEMPLATE)
class FilePreview(Gtk.Button):
class FilePreview(Gtk.Button, TypeView):
"""Preview for the core type File."""
__gtype_name__ = 'FilePreview'
......
......@@ -6,6 +6,7 @@ from gi.repository import Gtk, Adw
from base.file.file_view_model import FileViewModel
from blackfennec.presentation_system.type_view import TypeView
from blackfennec.util.change_notification import ChangeNotification
logger = logging.getLogger(__name__)
......@@ -15,7 +16,7 @@ UI_TEMPLATE = str(BASE_DIR.joinpath('file_view.ui'))
@Gtk.Template(filename=UI_TEMPLATE)
class FileView(Adw.PreferencesGroup):
class FileView(Adw.PreferencesGroup, TypeView):
"""View for the core type File."""
__gtype_name__ = 'FileView'
......
......@@ -2,14 +2,18 @@
from base.file.file_preview import FilePreview
from base.file.file_view_model import FileViewModel
from base.file.file_view import FileView
from blackfennec.interpretation.interpretation import Interpretation
from blackfennec.interpretation.specification import Specification
from blackfennec.type_system.interpretation.interpretation import Interpretation
from blackfennec.type_system.interpretation.specification import Specification
from blackfennec.presentation_system.type_view import TypeView
from blackfennec.presentation_system.type_view_factory import TypeViewFactory
class FileViewFactory:
class FileViewFactory(TypeViewFactory):
"""Creator of the FileView"""
def __init__(self, document_registry):
super().__init__()
self._document_registry = document_registry
......@@ -24,7 +28,7 @@ class FileViewFactory:
"""
return True
def create(self, interpretation: Interpretation) -> FileView:
def create(self, interpretation: Interpretation) -> TypeView:
"""creates a FileView
Args:
......
# -*- coding: utf-8 -*-
import logging
from blackfennec.interpretation.interpretation import Interpretation
from blackfennec.type_system.interpretation.interpretation import Interpretation
from blackfennec.document_system.document_registry import DocumentRegistry
from blackfennec.util.change_notification_dispatch_mixin import ChangeNotificationDispatchMixin
......
......@@ -6,6 +6,7 @@ from gi.repository import Gtk, Adw, Gdk
from base.image.image_view_model import ImageViewModel
from blackfennec.presentation_system.type_view import TypeView
from blackfennec.util.change_notification import ChangeNotification
logger = logging.getLogger(__name__)
......@@ -15,7 +16,7 @@ UI_TEMPLATE = str(BASE_DIR.joinpath('image_preview.ui'))
@Gtk.Template(filename=UI_TEMPLATE)
class ImagePreview(Gtk.Button):
class ImagePreview(Gtk.Button, TypeView):
"""Preview for the core type Image."""
__gtype_name__ = 'ImagePreview'
......
......@@ -6,6 +6,7 @@ from gi.repository import Gio, Gdk, Gtk, Adw
from base.image.image_view_model import ImageViewModel
from blackfennec.presentation_system.type_view import TypeView
from blackfennec.util.change_notification import ChangeNotification
logger = logging.getLogger(__name__)
......@@ -15,7 +16,7 @@ UI_TEMPLATE = str(BASE_DIR.joinpath('image_view.ui'))
@Gtk.Template(filename=UI_TEMPLATE)
class ImageView(Adw.PreferencesGroup):
class ImageView(Adw.PreferencesGroup, TypeView):
"""View for the core type Image."""
__gtype_name__ = 'ImageView'
......
......@@ -2,14 +2,17 @@
from base.image.image_preview import ImagePreview
from base.image.image_view_model import ImageViewModel
from base.image.image_view import ImageView
from blackfennec.interpretation.interpretation import Interpretation
from blackfennec.interpretation.specification import Specification
from blackfennec.type_system.interpretation.interpretation import Interpretation
from blackfennec.type_system.interpretation.specification import Specification
from blackfennec.presentation_system.type_view import TypeView
from blackfennec.presentation_system.type_view_factory import TypeViewFactory
class ImageViewFactory:
class ImageViewFactory(TypeViewFactory):
"""Creator of the ImageView"""
def __init__(self, document_registry):
super().__init__()
self._document_registry = document_registry
def satisfies(self, specification: Specification) -> bool:
......@@ -23,7 +26,7 @@ class ImageViewFactory:
"""
return True
def create(self, interpretation: Interpretation) -> ImageView:
def create(self, interpretation: Interpretation) -> TypeView:
"""creates a ImageView
Args:
......
......@@ -2,7 +2,7 @@
import logging
from base.image.image import Image
from blackfennec.interpretation.interpretation import Interpretation
from blackfennec.type_system.interpretation.interpretation import Interpretation
from blackfennec.document_system.document_registry import DocumentRegistry
from blackfennec.util.change_notification_dispatch_mixin import ChangeNotificationDispatchMixin
......
{
"super": { "$ref": "bftype://String"},
"type": "Url",
"pattern": "^(?:https?://)?(?:[\\w]+\\.)(?:\\.?[\\w]{2,})+($|/|\\?|#)",
"default": "https://blackfennec.org"
}
\ No newline at end of file
using Gtk 4.0;
using Adw 1;
template UrlPreview : Box {
Frame {
focusable: false;
visible: true;
valign: center;
margin-top: 8;
margin-bottom: 8;
TextView _value {
height-request: 32;
width-request: 128;
hexpand: true;
focusable: true;
wrap-mode: word_char;
left-margin: 8;
right-margin: 8;
top-margin: 8;
bottom-margin: 8;
}
}
Button {
focusable: true;
receives-default: true;
halign: end;
valign: center;
clicked => _on_open_clicked();
margin-bottom: 8;
margin-top: 8;
margin-start: 8;
icon-name: "web-browser-symbolic";
styles [
"flat",
"circular"
]
}
}
import os
from pathlib import Path
from base.url.url_view_model import UrlViewModel
from blackfennec.action_system.context import Context
from blackfennec.presentation_system.ui_service.message import Message
from blackfennec.presentation_system.ui_service.ui_service import UiService
from blackfennec.presentation_system.type_view import TypeView
from blackfennec.util.change_notification import ChangeNotification
from gi.repository import Gtk
BASE_DIR = Path(__file__).resolve().parent
UI_TEMPLATE = str(BASE_DIR.joinpath('url_preview.ui'))
@Gtk.Template(filename=UI_TEMPLATE)
class UrlPreview(Gtk.Box, TypeView):
__gtype_name__ = 'UrlPreview'
_value = Gtk.Template.Child()
def __init__(self, view_model: UrlViewModel, ui_service: UiService):
super().__init__()
self._view_model = view_model
self._ui_service = ui_service
self._view_model.bind(changed=self._update_value)
self._buffer_listener_paused = False
buffer = self._value.get_buffer()
buffer.set_text(self._view_model.url)
buffer.set_enable_undo(False)
buffer.connect('changed', self._on_buffer_changed)
self.connect('notify::active', self._on_activate)
@property
def text(self):
buffer = self._value.get_buffer()
start, end = buffer.get_bounds()
return buffer.get_text(start, end, False)
@text.setter
def text(self, text):
buffer = self._value.get_buffer()
start, end = buffer.get_bounds()
if buffer.get_text(start, end, False) == text:
return
self._buffer_listener_paused = True
buffer.set_text(text, len(text))
def _on_activate(self, unused_sender):
self._value.activate()
def _on_buffer_changed(self, buffer):
# This is a workaround for a bug in Gtk.TextView
# when we set the text in the buffer, the buffer-changed signal is
# emitted, twice. The first time with an empty url.
if self._buffer_listener_paused:
self._buffer_listener_paused = False
return
self._view_model.url = self.text
def _update_value(self, unused_sender, notification: ChangeNotification):
self.text = notification.new_value
@Gtk.Template.Callback()
def _on_open_clicked(self, unused_sender):
os.system(f"xdg-open {self.text}")
context = Context(self)
message = Message('Url opened')
self._ui_service.show_message(context, message)
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="UrlPreview" parent="GtkBox">
<child>
<object class="GtkFrame">
<property name="focusable">false</property>
<property name="visible">true</property>
<property name="valign">center</property>
<property name="margin-top">8</property>
<property name="margin-bottom">8</property>
<child>
<object class="GtkTextView" id="_value">
<property name="height-request">32</property>
<property name="width-request">128</property>
<property name="hexpand">true</property>
<property name="focusable">true</property>
<property name="wrap-mode">word-char</property>
<property name="left-margin">8</property>
<property name="right-margin">8</property>
<property name="top-margin">8</property>
<property name="bottom-margin">8</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton">
<property name="focusable">true</property>
<property name="receives-default">true</property>
<property name="halign">end</property>
<property name="valign">center</property>
<signal name="clicked" handler="_on_open_clicked"/>
<property name="margin-bottom">8</property>
<property name="margin-top">8</property>
<property name="margin-start">8</property>
<property name="icon-name">web-browser-symbolic</property>
<style>
<class name="flat"/>
<class name="circular"/>
</style>
</object>
</child>
</template>
</interface>
\ No newline at end of file