Verified Commit ac71290f authored by Todd Weaver's avatar Todd Weaver
Browse files

Adding fullscreen (lots of widgets to toggle/remember)

Mouse events to hide/show controls
Organizing flowbox list (children) to show/hide on fullscreen
App is still synchronous (initial threading attempt throws errors),
parent f4aa1b29
......@@ -31,34 +31,52 @@ Handy.init()
class ResultsBox(Gtk.Box):
__gtype_name__ = 'ResultsBox'
event_box = Gtk.Template.Child()
player_box = Gtk.Template.Child()
poster_image = Gtk.Template.Child()
controls_box = Gtk.Template.Child()
play = Gtk.Template.Child()
pause = Gtk.Template.Child()
slider = Gtk.Template.Child()
audio_dl = Gtk.Template.Child()
video_dl = Gtk.Template.Child()
speed = Gtk.Template.Child()
fullscreen = Gtk.Template.Child()
unfullscreen = Gtk.Template.Child()
details = Gtk.Template.Child()
title = Gtk.Template.Child()
channel = Gtk.Template.Child()
duration = Gtk.Template.Child()
def __init__(self, **kwargs):
window_to_player_box_padding = 28
def __init__(self, app_window, **kwargs):
super().__init__(**kwargs)
provider = Gtk.CssProvider()
provider.load_from_resource('/sm/puri/Stream/ui/results.css')
styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(
Gdk.Screen.get_default(), provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
self.app_window = app_window
# listen for motion on the player box for controls show/hide
self.event_box.add_events(Gdk.EventMask.POINTER_MOTION_MASK)
# determine window width at time of search
# do ratio calculation from width (16:9 or 1.77)
# retain aspect ratio
size = self.app_window.get_size()
self.app_orig_width = size.width
self.app_orig_height = size.height
self.video_box_width = int(size.width - self.window_to_player_box_padding)
self.video_box_height = int(self.video_box_width / 1.77)
# init gstreamer player
self.player = Gst.ElementFactory.make("playbin", "player")
self.sink = Gst.ElementFactory.make("gtksink")
video_widget = self.sink.get_property("widget")
video_widget.set_size_request(332, 186)
self.video_widget = self.sink.get_property("widget")
self.video_widget.set_size_request(self.video_box_width, self.video_box_height)
self.player_box.add(video_widget)
self.player_box.add(self.video_widget)
def get_duration(self, seconds):
m, s = divmod(seconds, 60)
......@@ -68,12 +86,6 @@ class ResultsBox(Gtk.Box):
else:
self.video_duration = f"{m:d}:{s:02d}"
def on_poster_load(self, source, async_res, user_data):
self.poster_image.clear()
pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(async_res)
self.poster_image.set_from_pixbuf(pixbuf)
def setup_stream(self, video_meta):
video_title = video_meta['title']
video_channel = video_meta['author']
......@@ -81,7 +93,6 @@ class ResultsBox(Gtk.Box):
poster_uri = video_meta['poster_uri']
self.get_duration(video_meta['lengthSeconds'])
self.title.set_label(video_title)
self.channel.set_label(video_channel)
self.duration.set_label(self.video_duration)
......@@ -93,13 +104,26 @@ class ResultsBox(Gtk.Box):
self.player.set_property("video-sink", self.sink)
poster_file = Gio.File.new_for_uri(poster_uri)
result = GdkPixbuf.Pixbuf.new_from_stream_at_scale_async(poster_file.read(),
332, 186, # width and height
True, # preserve_aspect_ratio
None, # cancellable
self.on_poster_load, # callback,
video_id) # user_data
# threading seems to error after the second invocation
#def on_poster_load(source, async_res, user_data):
# self.poster_image.clear()
# pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(async_res)
# self.poster_image.set_from_pixbuf(pixbuf)
#
#result = GdkPixbuf.Pixbuf.new_from_stream_at_scale_async(poster_file.read(),
# self.video_box_width, self.video_box_height,
# True, # preserve_aspect_ratio
# None, # cancellable
# on_poster_load, # callback
# video_id) # user_data
pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(poster_file.read(),
self.video_box_width, self.video_box_height,
True, # preserve_aspect_ratio
None) # cancellable
self.poster_image.clear()
self.poster_image.set_from_pixbuf(pixbuf)
def update_slider(self):
if not self.is_playing:
......@@ -150,7 +174,7 @@ class ResultsBox(Gtk.Box):
self.slider.set_sensitive(True)
# update slider to track video time in slider
GLib.timeout_add(1000, self.update_slider)
GLib.timeout_add_seconds(1, self.update_slider)
@Gtk.Template.Callback()
def pause_button(self, button):
......@@ -163,9 +187,58 @@ class ResultsBox(Gtk.Box):
def speed_button(self, button):
print("speed_button")
def resize_player(self, width, height):
self.poster_image.set_size_request(width, height)
self.video_widget.set_size_request(width, height)
def visible_children(self, visible, child_to_match):
children = self.app_window.results_list.get_children()
for child in children:
if child == child_to_match:
child.set_visible(True)
else:
child.set_visible(visible)
@Gtk.Template.Callback()
def fullscreen_button(self, button):
print("fullscreen_button")
self.fullscreen.set_visible(False)
self.unfullscreen.set_visible(True)
self.app_window.fullscreen()
self.app_window.search_bar_toggle.set_active(False)
self.app_window.search_bar.set_visible(False)
self.app_window.header_bar.set_visible(False)
self.visible_children(False, self.get_parent())
self.details.set_visible(False)
set_width = int(Gdk.Screen.get_default().get_width())
set_height = int(Gdk.Screen.get_default().get_height())
self.resize_player(set_width, set_height)
self.app_window.results_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER)
results_context = self.get_style_context()
results_context.remove_class("results")
results_context.add_class("fullscreen")
@Gtk.Template.Callback()
def unfullscreen_button(self, button):
self.fullscreen.set_visible(True)
self.unfullscreen.set_visible(False)
self.app_window.unfullscreen()
self.app_window.search_bar_toggle.set_active(True)
self.app_window.search_bar.set_visible(True)
self.app_window.header_bar.set_visible(True)
self.visible_children(True, None)
self.details.set_visible(True)
results_context = self.get_style_context()
results_context.remove_class("fullscreen")
results_context.add_class("results")
set_width = int(self.app_orig_width - self.window_to_player_box_padding)
set_height = int(set_width / 1.77)
self.resize_player(set_width, set_height)
self.app_window.resize(self.app_orig_width, self.app_orig_height)
self.app_window.results_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
@Gtk.Template.Callback()
def seek_slider(self, scale):
......@@ -175,3 +248,13 @@ class ResultsBox(Gtk.Box):
self.player.seek_simple(Gst.Format.TIME,
Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
seek * Gst.SECOND / self.percent)
def poll_mouse(self):
if self.controls_box.get_visible():
self.controls_box.set_visible(False)
@Gtk.Template.Callback()
def mouse_move(self, event, data):
if not self.controls_box.get_visible():
self.controls_box.set_visible(True)
GLib.timeout_add_seconds(3, self.poll_mouse)
......@@ -24,6 +24,7 @@ import json
class Search:
def __init__(self, **kwargs):
self.app_window = kwargs.get('app_window', None)
self.query = kwargs.get('query', None)
self.headers = ({
......
......@@ -3,7 +3,8 @@
<gresource prefix="/sm/puri/Stream">
<file>ui/window.ui</file>
<file>ui/results.ui</file>
<file>ui/results.css</file>
<file>ui/stream.css</file>
<file>ui/video-placeholder-332x186.png</file>
<file alias="gtk/help-overlay.ui">ui/help-overlay.ui</file>
</gresource>
</gresources>
......@@ -12,13 +12,9 @@
<property name="can-focus">False</property>
<property name="halign">center</property>
<property name="valign">start</property>
<property name="margin-start">4</property>
<property name="margin-end">4</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<property name="baseline-position">top</property>
<signal name="motion-notify-event" handler="mouse_move" swapped="no"/>
<child>
<object class="GtkOverlay" id="video_overlay">
<property name="visible">True</property>
......@@ -27,22 +23,20 @@
<property name="valign">center</property>
<child>
<object class="GtkBox" id="player_box">
<property name="width-request">332</property>
<property name="height-request">186</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="hexpand">False</property>
<property name="vexpand">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkImage" id="poster_image">
<property name="width-request">332</property>
<property name="height-request">186</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">False</property>
<property name="icon-name">video-x-generic</property>
<property name="resource">/sm/puri/Stream/ui/video-placeholder-332x186.png</property>
</object>
<packing>
<property name="expand">False</property>
......@@ -56,10 +50,28 @@
</packing>
</child>
<child type="overlay">
<object class="GtkBox">
<object class="GtkEventBox" id="event_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="above-child">True</property>
<signal name="enter-notify-event" handler="mouse_enter" swapped="no"/>
<signal name="motion-notify-event" handler="mouse_move" swapped="no"/>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="pass-through">True</property>
</packing>
</child>
<child type="overlay">
<object class="GtkBox" id="controls_box">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="valign">end</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
......@@ -74,7 +86,7 @@
<property name="can-focus">False</property>
<property name="spacing">8</property>
<child>
<object class="GtkButton" id="audio-dl">
<object class="GtkButton" id="audio_dl">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
......@@ -100,7 +112,7 @@
</packing>
</child>
<child>
<object class="GtkButton" id="video-dl">
<object class="GtkButton" id="video_dl">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
......@@ -246,24 +258,58 @@
</packing>
</child>
<child>
<object class="GtkButton" id="fullscreen">
<object class="GtkStack">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Fullscreen</property>
<property name="halign">center</property>
<property name="valign">center</property>
<signal name="clicked" handler="fullscreen_button" swapped="no"/>
<property name="can-focus">False</property>
<child>
<object class="GtkImage">
<object class="GtkButton" id="fullscreen">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">view-fullscreen-symbolic</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Fullscreen</property>
<property name="halign">center</property>
<property name="valign">center</property>
<signal name="clicked" handler="fullscreen_button" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">view-fullscreen-symbolic</property>
</object>
</child>
<style>
<class name="rounded-button"/>
</style>
</object>
<packing>
<property name="name">fullscreen</property>
</packing>
</child>
<child>
<object class="GtkButton" id="unfullscreen">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Unfullscreen</property>
<property name="halign">center</property>
<property name="valign">center</property>
<signal name="clicked" handler="unfullscreen_button" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">view-fullscreen-symbolic</property>
</object>
</child>
<style>
<class name="rounded-button"/>
</style>
</object>
<packing>
<property name="name">unfullscreen</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="rounded-button"/>
</style>
</object>
<packing>
<property name="expand">False</property>
......@@ -306,6 +352,9 @@
</packing>
</child>
</object>
<packing>
<property name="index">1</property>
</packing>
</child>
</object>
<packing>
......@@ -327,7 +376,7 @@
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Title</property>
<property name="label" translatable="yes">...</property>
<property name="ellipsize">end</property>
<attributes>
<attribute name="weight" value="bold"/>
......@@ -350,7 +399,7 @@
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Channel</property>
<property name="label" translatable="yes">...</property>
<property name="ellipsize">end</property>
</object>
<packing>
......@@ -364,7 +413,7 @@
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">0:00</property>
<property name="label" translatable="yes">...</property>
<property name="justify">right</property>
<property name="ellipsize">end</property>
<attributes>
......
.results {
border: 1px solid gray;
margin: 0px;
margin: 4px;
margin-bottom: 12px;
padding: 6px;
padding-bottom: 12px;
border-radius: 8px;
......@@ -9,6 +10,13 @@
background-color: white;
}
.fullscreen {
border: 0px solid green;
margin: 0px;
padding: 0px;
background-color: black;
}
button.rounded-button {
padding: 4px;
border-radius: 9999px;
......
......@@ -11,13 +11,14 @@
<property name="vexpand">True</property>
<property name="default-width">360</property>
<property name="default-height">720</property>
<signal name="key-press-event" handler="keypress_listener" swapped="no"/>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="HdyHeaderBar">
<object class="HdyHeaderBar" id="header_bar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="title" translatable="yes">Stream</property>
......@@ -89,7 +90,7 @@
<property name="primary-icon-name">edit-find-symbolic</property>
<property name="primary-icon-activatable">False</property>
<property name="primary-icon-sensitive">False</property>
<signal name="search-changed" handler="search_entry" swapped="no"/>
<signal name="activate" handler="search_entry" swapped="no"/>
</object>
</child>
</object>
......@@ -120,14 +121,16 @@
</child>
<child>
<object class="GtkScrolledWindow" id="results_window">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">False</property>
<property name="vexpand">True</property>
<property name="vexpand">False</property>
<property name="hscrollbar-policy">never</property>
<child>
<object class="GtkFlowBox" id="results_list">
<property name="can-focus">False</property>
<property name="visible">True</property>
<property name="vexpand">False</property>
<property name="hexpand">False</property>
<property name="max-children-per-line">1</property>
<property name="selection-mode">none</property>
......
......@@ -16,9 +16,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import gi
gi.require_version('Gdk', '3.0')
gi.require_version('Gio', '2.0')
gi.require_version('Gtk', '3.0')
gi.require_version('Handy', '1')
from gi.repository import Gtk, Handy
from gi.repository import Gdk, Gio, Gtk, Handy
Handy.init()
......@@ -29,6 +31,8 @@ from .search import Search
class StreamWindow(Handy.ApplicationWindow):
__gtype_name__ = 'StreamWindow'
header_bar = Gtk.Template.Child()
search_bar_toggle = Gtk.Template.Child()
search_bar = Gtk.Template.Child()
......@@ -37,7 +41,7 @@ class StreamWindow(Handy.ApplicationWindow):
results_list = Gtk.Template.Child()
@Gtk.Template.Callback()
def search_toggle(self, widget):
def search_toggle(self, toggle_button):
# toggle the True/False from what is current
self.search_bar.set_visible(self.search_bar_toggle.get_active())
......@@ -46,25 +50,46 @@ class StreamWindow(Handy.ApplicationWindow):
for child in children:
child.destroy()
@Gtk.Template.Callback()
def search_entry(self, widget):
self.status_page.set_visible(False)
self.results_window.set_visible(True)
def do_search(self, task, source_obj, task_data, cancellable):
search_results = Search(app_window = self, query = self.search_query)
self.results_window.set_visible(True)
self.clear_entries()
# get search results
search_results = Search(query = widget.get_text())
# iterate per video row
for video_meta in search_results.json:
results_box = ResultsBox()
results_box = ResultsBox(self)
self.results_list.add(results_box)
results_box.setup_stream(video_meta)
# one entry for now
break
@Gtk.Template.Callback()
def search_entry(self, search_box):
self.status_page.set_visible(False)
self.search_query = search_box.get_text()
# threading throws errors
#
# for api legibility
#stub_data = None
#stub_callback = None
#stub_cancellable = None
#search_task = Gio.Task.new(self, stub_cancellable, stub_callback, stub_data)
#
#search_task.run_in_thread(self.do_search)
self.do_search(None, None, None, None)
# @Gtk.Template.Callback()
# def keypress_listener(self, widget, ev):
# key = Gdk.keyval_name(ev.keyval)
# if key == "k":
# # do stuff
def __init__(self, **kwargs):
super().__init__(**kwargs)
provider = Gtk.CssProvider()
provider.load_from_resource('/sm/puri/Stream/ui/stream.css')
styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(
Gdk.Screen.get_default(), provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment