浏览代码

Add very very MVP and buggy PPVM playback

Billy Barrow 2 年之前
父节点
当前提交
854d30bb29

+ 1 - 0
src/App.vala

@@ -26,6 +26,7 @@ namespace PpubViewer {
 
     int main (string[] argv) {
         // Create a new application
+        Gst.init (ref argv);
         var app = new PpubViewerApplication();
         return app.run (argv);
     }

+ 102 - 0
src/DocumentView/DocumentViewManager.vala

@@ -0,0 +1,102 @@
+using Adw;
+using Gtk;
+
+namespace PpubViewer.DocumentView {
+
+    public class DocumentViewManager : Box, DocumentViewWidget {
+
+        private StatusPage status_page;
+
+        private DocumentViewWidget current_view;
+        private MarkdownView markdown_view;
+        private PlainTextView text_view;
+        private UnsupportedView unsupported_view;
+        private VideoView video_view;
+
+        public DocumentViewManager() {
+            orientation = Orientation.VERTICAL;
+
+            status_page = new StatusPage();
+            status_page.vexpand = true;
+            status_page.title = "PPUB Viewer";
+            status_page.description = "Open a PPUB to view it here";
+            status_page.icon_name = "text-x-generic-symbolic";
+
+            markdown_view = new MarkdownView();
+            markdown_view.link_clicked.connect(url => link_clicked(url));
+
+            text_view = new PlainTextView();
+            text_view.link_clicked.connect(url => link_clicked(url));
+
+            unsupported_view = new UnsupportedView();
+            unsupported_view.link_clicked.connect(url => link_clicked(url));
+
+            video_view = new VideoView();
+            video_view.link_clicked.connect(url => link_clicked(url));
+            
+            append(status_page);
+            append(markdown_view);
+            append(text_view);
+            append(unsupported_view);
+            append(video_view);
+
+            hide_all();
+            status_page.visible = true;
+        }
+
+        public double get_scroll_position() {
+            return current_view.get_scroll_position();
+        }
+
+        public void set_zoom_percentage(int percent) {
+            markdown_view.set_zoom_percentage(percent);
+        }
+
+        public void set_scroll_position(double position) {
+            current_view.set_scroll_position(position);
+        }
+
+        public void load_asset(Ppub.Publication publication, Ppub.Asset asset) {
+            if(asset.mimetype == "text/markdown") {
+                set_view(markdown_view);
+                markdown_view.load_asset(publication, asset);
+            }
+            else if(asset.mimetype == "application/x-ppvm") {
+                set_view(video_view);
+                video_view.load_asset(publication, asset);
+            }
+            else if(asset.mimetype.has_prefix("text/")) {
+                set_view(text_view);
+                text_view.load_asset(publication, asset);
+            }
+            else {
+                set_view(unsupported_view);
+                unsupported_view.load_asset(publication, asset);
+            }
+        }
+
+        private void hide_all() {
+            markdown_view.visible = false;
+            status_page.visible = false;
+            text_view.visible = false;
+            unsupported_view.visible = false;
+            video_view.visible = false;
+        }
+
+        private void set_view(DocumentViewWidget view) {
+            hide_all();
+            current_view = view;
+            view.visible = true;
+        }
+
+    }
+
+    public interface DocumentViewWidget : Widget {
+        public abstract double get_scroll_position();
+        public abstract void set_zoom_percentage(int percent);
+        public abstract void set_scroll_position(double position);
+        public abstract void load_asset(Ppub.Publication publication, Ppub.Asset asset);
+        public signal void link_clicked(string href);
+    }
+
+}

+ 65 - 0
src/DocumentView/MarkdownView.vala

@@ -0,0 +1,65 @@
+using Gtk;
+using Adw;
+using GtkCommonMark;
+
+namespace PpubViewer.DocumentView {
+
+    public class MarkdownView : Box, DocumentViewWidget {
+
+        protected ScrolledWindow scrolled_window;
+        protected ClampScrollable clamp;
+        protected GtkCommonMark.MarkdownView markdown_view;
+        private Ppub.Publication publication;
+
+        public MarkdownView() {
+            orientation = Orientation.VERTICAL;
+            scrolled_window = new ScrolledWindow ();
+            clamp = new ClampScrollable ();
+            clamp.maximum_size = 800;
+            scrolled_window.child = clamp;
+            scrolled_window.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
+            scrolled_window.vexpand = true;
+
+            markdown_view = new GtkCommonMark.MarkdownView ();
+            markdown_view.set_wrap_mode (WrapMode.WORD_CHAR);
+            markdown_view.widget_embedded.connect(widget_embedded);
+            markdown_view.link_activated.connect(url => link_clicked(url));
+            clamp.child = markdown_view;
+
+            append(scrolled_window);
+        }
+
+        public double get_scroll_position () {
+            return scrolled_window.vadjustment.get_value ();
+        }
+
+        public void set_zoom_percentage (int percent) {
+            var scale = (float)percent / 100f;
+            markdown_view.tag_manager.font_scale = scale;
+        }
+
+        public void set_scroll_position (double position) {
+            scrolled_window.vadjustment.set_value (position);
+        }
+        
+        public virtual void load_asset (Ppub.Publication publication, Ppub.Asset asset) {
+            this.publication = publication;
+            markdown_view.buffer.set_text("", 0);
+            markdown_view.load_from_stream (publication.read_asset (asset.name));
+        }
+
+        protected void widget_embedded(GtkCommonMark.MarkdownViewEmbeddedWidgetHost widget, string file, string title) {
+            var pixbuf = new Gdk.Pixbuf.from_stream (publication.read_asset (file));
+            var image = new Gtk.Picture.for_pixbuf (pixbuf);
+            image.content_fit = Gtk.ContentFit.FILL;
+            widget.available_width_changed.connect(wid => {
+                image.width_request = wid;
+                var ratio = image.get_paintable().get_intrinsic_aspect_ratio ();
+                image.height_request = (int)(wid / ratio);
+            });
+            widget.append (image);
+        }
+
+    }
+
+}

+ 58 - 0
src/DocumentView/PlainTextView.vala

@@ -0,0 +1,58 @@
+using Gtk;
+using Adw;
+using GtkCommonMark;
+
+namespace PpubViewer.DocumentView {
+
+    public class PlainTextView : Box, DocumentViewWidget {
+
+        private ScrolledWindow scrolled_window;
+        private ClampScrollable clamp;
+        private TextView text_view;
+
+        public PlainTextView() {
+            orientation = Orientation.VERTICAL;
+            scrolled_window = new ScrolledWindow ();
+            clamp = new ClampScrollable ();
+            clamp.maximum_size = 800;
+            scrolled_window.child = clamp;
+            scrolled_window.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
+            scrolled_window.vexpand = true;
+
+            text_view = new TextView ();
+            text_view.editable = false;
+            text_view.monospace = true;
+            text_view.set_wrap_mode (WrapMode.WORD_CHAR);
+            text_view.top_margin = 18;
+            text_view.bottom_margin = 18;
+            text_view.left_margin = 18;
+            text_view.right_margin = 18;
+            clamp.child = text_view;
+
+            append(scrolled_window);
+        }
+
+        public double get_scroll_position () {
+            return scrolled_window.vadjustment.get_value ();
+        }
+
+        public void set_zoom_percentage (int percent) {
+            var scale = (float)percent / 100f;
+            // TODO
+        }
+
+        public void set_scroll_position (double position) {
+            scrolled_window.vadjustment.set_value (position);
+        }
+        
+        public void load_asset (Ppub.Publication publication, Ppub.Asset asset) {
+            MemoryOutputStream os = new MemoryOutputStream (null, GLib.realloc, GLib.free);
+            os.splice (publication.read_asset (asset.name), OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET);
+            var text = os.steal_data ();
+            text.length = (int) os.get_data_size ();
+            text_view.buffer.set_text((string)text, text.length);
+        }
+
+    }
+
+}

+ 37 - 0
src/DocumentView/UnsupportedView.vala

@@ -0,0 +1,37 @@
+using Adw;
+using Gtk;
+
+namespace PpubViewer.DocumentView {
+
+    public class UnsupportedView : Box, DocumentViewWidget {
+
+        private StatusPage status_page;
+
+        public double get_scroll_position () {
+            return 0;
+        }
+        public void set_zoom_percentage (int percent) {
+            return;
+        }
+        public void set_scroll_position (double position) {
+            return;
+        }
+
+        public void load_asset (Ppub.Publication publication, Ppub.Asset asset) {
+            status_page.description = @"Files of type '$(asset.mimetype)' cannot be displayed by this application.";
+        }
+
+        public UnsupportedView() {
+            orientation = Orientation.VERTICAL;
+
+            status_page = new StatusPage();
+            status_page.vexpand = true;
+            status_page.title = "Unsupported Content Type";
+            status_page.icon_name = "face-uncertain-symbolic";
+
+            append(status_page);
+
+        }
+
+    }
+}

+ 47 - 0
src/DocumentView/VideoView.vala

@@ -0,0 +1,47 @@
+using Gtk;
+using Adw;
+using GtkCommonMark;
+
+namespace PpubViewer.DocumentView {
+
+    public class VideoView : MarkdownView {
+
+        private Box box;
+        private Ppub.VideoManifest ppvm;
+        private Video video;
+        private MediaStream current_stream;
+
+        public VideoView() {
+            base();
+
+            box = new Box(Orientation.VERTICAL, 8);
+            scrolled_window.child = box;
+            markdown_view.vexpand = true;
+            
+            video = new Video();
+            box.append (video);
+            box.append (clamp);
+
+        }
+
+        public override void load_asset (Ppub.Publication publication, Ppub.Asset asset) {
+
+            var stream = publication.read_asset (asset.name);
+            ppvm = new Ppub.VideoManifest.from_stream (stream);
+
+            var s = publication.read_asset(ppvm.streams.first().file_name);
+            var tmp = File.new_for_path ("/tmp/video");
+            var ts = tmp.create (FileCreateFlags.REPLACE_DESTINATION);
+            ts.splice (s, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET);
+
+            var mf = MediaFile.for_file (tmp);
+            video.set_media_stream(mf);
+        
+            print(@"Error: $(mf.error?.message)\n");
+            
+            var description_asset = publication.get_asset (ppvm.description_file_name);
+            base.load_asset (publication, description_asset);
+        }
+
+    }
+}

+ 1 - 1
src/PropertiesPane.vala

@@ -15,7 +15,7 @@ namespace PpubViewer {
             margin_end = 18;
             margin_bottom = 18;
 
-            width_request = 180;
+            width_request = 250;
             halign = Align.START;
             set_metadata(metadata);
         }

+ 27 - 54
src/Window.vala

@@ -9,9 +9,7 @@ namespace PpubViewer {
         private Adw.HeaderBar header_bar;
         private Box box;
         private Flap flap;
-        private ClampScrollable clamp;
-        private GtkCommonMark.MarkdownView markdown_view;
-        private ScrolledWindow scrolled_window;
+        private DocumentView.DocumentViewManager document_view;
 
         private Button back_button;
         private Button forward_button;
@@ -19,7 +17,7 @@ namespace PpubViewer {
 
         private Button menu_button;
         private PopoverMenu menu;
-        private SpinButton zoom_spin;
+        private ZoomSpinButton zoom_spin;
 
         private Ppub.Publication publication;
         private NavigationHistoryItem current_page = null;
@@ -35,8 +33,8 @@ namespace PpubViewer {
             box.append (flap);
             content = box;
             header_bar.show_end_title_buttons = true;
-            default_width = 600;
-            default_height = 400;
+            default_width = 900;
+            default_height = 700;
             header_bar.title_widget = window_title;
             flap.separator = new Separator(Orientation.VERTICAL);
 
@@ -58,31 +56,20 @@ namespace PpubViewer {
             menu_button = new Button.from_icon_name("open-menu-symbolic");
             menu_button.clicked.connect(() => menu.popdown());
             header_bar.pack_end(menu_button);
-            var zoom_adjustment = new Adjustment(100, 80, 300, 10, 50, 0);
-            zoom_adjustment.value_changed.connect(change_zoom);
-            zoom_spin = new SpinButton(zoom_adjustment, 1, 0);
-            header_bar.pack_end(zoom_spin);
-            
 
-            scrolled_window = new ScrolledWindow();
-            scrolled_window.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
-            scrolled_window.vexpand = true;
-            clamp = new ClampScrollable ();
-            clamp.maximum_size = 800;
-            scrolled_window.child = clamp;
-
-            var status_page = new StatusPage();
-            status_page.vexpand = true;
-            status_page.title = "PPUB Viewer";
-            status_page.description = "Open a PPUB to view it here";
-            status_page.icon_name = "text-x-generic-symbolic";
-            flap.content = status_page;
-
-            markdown_view = new GtkCommonMark.MarkdownView ();
-            markdown_view.set_wrap_mode (WrapMode.WORD_CHAR);
-            markdown_view.widget_embedded.connect(widget_embedded);
-            markdown_view.link_activated.connect(link_clicked);
-            clamp.child = markdown_view;
+            var zoom_squeezer = new Squeezer();
+            zoom_squeezer.transition_type = SqueezerTransitionType.CROSSFADE;
+            zoom_spin = new ZoomSpinButton(false);
+            var zoom_spin_small = new ZoomSpinButton(true);
+            zoom_spin_small.adjustment = zoom_spin.adjustment;
+            zoom_squeezer.add(zoom_spin);
+            zoom_squeezer.add(zoom_spin_small);
+            zoom_spin.zoom_level_changed.connect(change_zoom);
+            header_bar.pack_end(zoom_squeezer);
+
+            document_view = new DocumentView.DocumentViewManager();
+            document_view.link_clicked.connect(link_clicked);
+            flap.content = document_view;
             
             Gtk.Settings.get_default().notify["gtk-application-prefer-dark-theme"].connect(() => configure_tags());
 
@@ -119,20 +106,20 @@ namespace PpubViewer {
                 current_page.scroll_position = 0;
             }
             else {
-                current_page = current_page.add_next(file, scrolled_window.vadjustment.get_value());
+                current_page = current_page.add_next(file, document_view.get_scroll_position());
             }
 
             load_document(file);
         }
 
         public void go_back() {
-            current_page.scroll_position = scrolled_window.vadjustment.get_value();
+            current_page.scroll_position = document_view.get_scroll_position();
             current_page = current_page.previous;
             load_document(current_page.file_name);
         }
 
         public void go_forward() {
-            current_page.scroll_position = scrolled_window.vadjustment.get_value();
+            current_page.scroll_position = document_view.get_scroll_position();
             current_page = current_page.next;
             load_document(current_page.file_name);
         }
@@ -143,28 +130,15 @@ namespace PpubViewer {
             back_button.sensitive = current_page.previous != null;
             forward_button.sensitive = current_page.next != null;            
 
-            flap.content = scrolled_window;
-            markdown_view.buffer.set_text("", 0);
-            markdown_view.load_from_stream (publication.read_asset (file));
+            document_view.load_asset(publication, publication.get_asset(file));
+
             Idle.add_once(() => {
                 configure_tags();
-                scrolled_window.vadjustment.set_value(current_page.scroll_position);
+                document_view.set_scroll_position(current_page.scroll_position);
                 return false;
             });
         }
 
-        private void widget_embedded(GtkCommonMark.MarkdownViewEmbeddedWidgetHost widget, string file, string title) {
-            var pixbuf = new Gdk.Pixbuf.from_stream (publication.read_asset (file));
-            var image = new Gtk.Picture.for_pixbuf (pixbuf);
-            image.content_fit = Gtk.ContentFit.FILL;
-            widget.available_width_changed.connect(wid => {
-                image.width_request = wid;
-                var ratio = image.get_paintable().get_intrinsic_aspect_ratio ();
-                image.height_request = (int)(wid / ratio);
-            });
-            widget.append (image);
-        }
-
         private void link_clicked(string url) {
 
             var asset = publication.get_asset(Uri.unescape_string(url));
@@ -217,7 +191,8 @@ namespace PpubViewer {
 
         private void configure_tags() {
             var link = new LinkButton("");
-            markdown_view.tag_manager.update_link_colour(link.get_color());
+            //  markdown_view.tag_manager.update_link_colour(link.get_color());
+            // TODO
         }
 
         private void property_link_clicked(string file) {
@@ -227,10 +202,8 @@ namespace PpubViewer {
             }
         }
 
-        private void change_zoom() {
-            var scale = zoom_spin.adjustment.value / 100;
-            markdown_view.tag_manager.font_scale = (float)scale;
-
+        private void change_zoom(int percent) {
+            document_view.set_zoom_percentage(percent);
         }
 
     }

+ 63 - 0
src/ZoomSpinButton.vala

@@ -0,0 +1,63 @@
+using Adw;
+using Gtk;
+
+namespace PpubViewer {
+
+    public class ZoomSpinButton : Gtk.Box {
+
+        private Button zoom_out_button;
+        private Button reset_zoom_button;
+        private Button zoom_in_button;
+
+        public Adjustment adjustment {get; set;}
+
+        public int default_value {get; set;}
+
+        public ZoomSpinButton(bool compact) {
+            orientation = Orientation.HORIZONTAL;
+            halign = Align.END;
+
+            
+            zoom_out_button = new Button.from_icon_name ("zoom-out-symbolic");
+            zoom_in_button = new Button.from_icon_name ("zoom-in-symbolic");
+            adjustment = new Adjustment(100, 80, 300, 10, 50, 0);
+            default_value = 100;
+            
+            
+            adjustment.value_changed.connect(() => zoom_level_changed((int)adjustment.value));
+            
+            append(zoom_out_button);
+            if(!compact) {
+                reset_zoom_button = new Button();
+                append(reset_zoom_button);
+                add_css_class("linked");
+                zoom_level_changed.connect(update_label);
+                update_label(default_value);
+            }
+            append(zoom_in_button);
+            
+            zoom_in_button.clicked.connect(zoom_in);
+            zoom_out_button.clicked.connect(zoom_out);
+            reset_zoom_button.clicked.connect(reset_zoom);
+
+        }
+
+        public signal void zoom_level_changed(int percentage);
+
+        private void update_label(int percentage) {
+            reset_zoom_button.label = @"$percentage%";
+        }
+
+        public void zoom_in() {
+            adjustment.value += adjustment.step_increment;
+        }
+
+        public void zoom_out() {
+            adjustment.value -= adjustment.step_increment;
+        }
+
+        public void reset_zoom() {
+            adjustment.value = default_value;
+        }
+    }
+}

+ 8 - 1
src/meson.build

@@ -6,6 +6,12 @@ sources = files('App.vala')
 sources += files('Window.vala')
 sources += files('NavigationStack.vala')
 sources += files('PropertiesPane.vala')
+sources += files('ZoomSpinButton.vala')
+sources += files('DocumentView/DocumentViewManager.vala')
+sources += files('DocumentView/MarkdownView.vala')
+sources += files('DocumentView/PlainTextView.vala')
+sources += files('DocumentView/VideoView.vala')
+sources += files('DocumentView/UnsupportedView.vala')
 
 dependencies = [
     dependency('glib-2.0'),
@@ -16,7 +22,8 @@ dependencies = [
     dependency('invercargill'),
     dependency('gtk4'),
     dependency('gtkcommonmark'),
-    dependency('libppub')
+    dependency('libppub'),
+    dependency('gstreamer-1.0')
 ]
 
 executable('ppub-viewer', sources, dependencies: dependencies)