Changes
8 changed files (+274/-85)
-
-
@@ -64,6 +64,11 @@ # > The GTK toolkit# https://docs.gtk.org/gtk4/ gtk4 # Runtime dependency. # Faster (using SIMD) libjpeg implementation # https://libjpeg-turbo.org/ libjpeg # Provides styles and widgets following GNOME Human Interface Guideline. # Runtime dependency. # (https://developer.gnome.org/hig/index.html)
-
-
-
@@ -0,0 +1,21 @@/* * Copyright 2025 Shota FUJI * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 */ .plac-playback-artwork { border-radius: 3px; }
-
-
-
@@ -37,5 +37,6 @@ <file preprocess="xml-stripblanks">icons/scalable/actions/play-large-symbolic.svg</file><file preprocess="xml-stripblanks">icons/scalable/actions/skip-backward-large-symbolic.svg</file> <file preprocess="xml-stripblanks">icons/scalable/actions/skip-forward-large-symbolic.svg</file> <file preprocess="xml-stripblanks">icons/scalable/actions/sound-symbolic.svg</file> <file>css/main-window.css</file> </gresource> </gresources>
-
-
-
@@ -18,121 +18,135 @@ SPDX-License-Identifier: Apache-2.0--> <interface> <template class="PlacGtkAdwaitaPlaybackToolbar" parent="GtkBox"> <property name="orientation">vertical</property> <property name="orientation">horizontal</property> <property name="halign">center</property> <property name="valign">end</property> <property name="spacing">2</property> <property name="spacing">12</property> <property name="margin-top">8</property> <property name="margin-bottom">4</property> <property name="margin-start">12</property> <property name="margin-end">12</property> <child> <object class="PlacGtkAdwaitaArtwork" id="artwork"> <property name="width">80</property> <binding name="height"> <lookup name="width">artwork</lookup> </binding> </object> </child> <child> <object class="GtkBox"> <property name="orientation">horizontal</property> <property name="margin-start">12</property> <property name="margin-end">12</property> <property name="orientation">vertical</property> <property name="spacing">8</property> <child> <object class="GtkBox"> <property name="orientation">vertical</property> <property name="valign">center</property> <property name="hexpand">true</property> <property name="orientation">horizontal</property> <property name="spacing">8</property> <child> <object class="GtkLabel" id="playing_line1"> <property name="halign">start</property> <property name="ellipsize">end</property> <property name="label"></property> <object class="GtkBox"> <property name="orientation">vertical</property> <property name="valign">center</property> <property name="hexpand">true</property> <child> <object class="GtkLabel" id="playing_line1"> <property name="halign">start</property> <property name="ellipsize">end</property> <property name="label"></property> </object> </child> <child> <object class="GtkLabel" id="playing_line2"> <style> <class name="dimmed" /> <!-- ~libadwaita@1.7 --> <class name="dim-label" /> </style> <property name="halign">start</property> <property name="ellipsize">end</property> <property name="label"></property> </object> </child> </object> </child> <child> <object class="GtkLabel" id="playing_line2"> <object class="GtkMenuButton"> <style> <class name="dimmed" /> <!-- ~libadwaita@1.7 --> <class name="dim-label" /> <class name="flat" /> </style> <property name="halign">start</property> <property name="ellipsize">end</property> <property name="label"></property> <property name="label">Zone</property> <property name="popover"> <object class="GtkPopover" id="zone_list_popover"> <child> <object class="GtkListBox" id="zone_list"> <style> <class name="navigation-sidebar" /> </style> </object> </child> </object> </property> </object> </child> </object> </child> <child> <object class="GtkMenuButton"> <style> <class name="flat" /> </style> <property name="label">Zone</property> <property name="popover"> <object class="GtkPopover" id="zone_list_popover"> <object class="GtkBox"> <property name="orientation">horizontal</property> <property name="margin-start">3</property> <property name="margin-end">12</property> <property name="spacing">8</property> <child> <object class="GtkBox" id="controls"> <property name="orientation">horizontal</property> <property name="spacing">2</property> <child> <object class="GtkListBox" id="zone_list"> <object class="GtkButton" id="prev"> <style> <class name="navigation-sidebar" /> <class name="flat" /> </style> <property name="label">Previous</property> <property name="icon-name">skip-backward-large-symbolic</property> <property name="action-name">prev_current_zone</property> </object> </child> </object> </property> </object> </child> </object> </child> <child> <object class="GtkBox"> <property name="orientation">horizontal</property> <property name="margin-start">3</property> <property name="margin-end">12</property> <property name="spacing">8</property> <child> <object class="GtkBox" id="controls"> <property name="orientation">horizontal</property> <property name="spacing">2</property> <child> <object class="GtkButton" id="prev"> <style> <class name="flat" /> </style> <property name="label">Previous</property> <property name="icon-name">skip-backward-large-symbolic</property> <property name="action-name">prev_current_zone</property> </object> </child> <child> <object class="GtkButton" id="play"> <style> <class name="flat" /> </style> <property name="label">Play</property> <property name="icon-name">play-large-symbolic</property> <property name="action-name">play_current_zone</property> </object> </child> <child> <object class="GtkButton" id="pause"> <style> <class name="flat" /> </style> <property name="label">Pause</property> <property name="icon-name">pause-large-symbolic</property> <property name="action-name">pause_current_zone</property> <child> <object class="GtkButton" id="play"> <style> <class name="flat" /> </style> <property name="label">Play</property> <property name="icon-name">play-large-symbolic</property> <property name="action-name">play_current_zone</property> </object> </child> <child> <object class="GtkButton" id="pause"> <style> <class name="flat" /> </style> <property name="label">Pause</property> <property name="icon-name">pause-large-symbolic</property> <property name="action-name">pause_current_zone</property> </object> </child> <child> <object class="GtkButton" id="next"> <style> <class name="flat" /> </style> <property name="label">Skip Next</property> <property name="icon-name">skip-forward-large-symbolic</property> <property name="action-name">next_current_zone</property> </object> </child> </object> </child> <child> <object class="GtkButton" id="next"> <style> <class name="flat" /> </style> <property name="label">Skip Next</property> <property name="icon-name">skip-forward-large-symbolic</property> <property name="action-name">next_current_zone</property> <object class="GtkScale" id="seek"> <property name="hexpand">true</property> <property name="orientation">horizontal</property> </object> </child> </object> </child> <child> <object class="GtkScale" id="seek"> <property name="hexpand">true</property> <property name="orientation">horizontal</property> </object> </child> </object>
-
-
-
@@ -159,5 +159,18 @@yield; return (owned) result; } public async Image.GetResult? get_image(string image_key, Image.GetOptions options) { GLib.SourceFunc callback = get_image.callback; Image.GetResult? result = null; new GLib.Thread<void>("get_image", () => { result = conn.get_image(image_key, options); GLib.Idle.add((owned) callback); }); yield; return (owned) result; } } }
-
-
-
@@ -0,0 +1,120 @@// Copyright 2025 Shota FUJI // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 namespace PlacGtkAdwaita { class Artwork : Gtk.Box { private uint16 _width = 100; public uint16 width { get { return _width; } construct set { _width = value; width_request = (int) value; } } private uint16 _height = 100; public uint16 height { get { return _height; } construct set { _height = value; height_request = (int) value; } } private Plac.AsyncConnection? _conn = null; public Plac.AsyncConnection? conn { get { return _conn; } set { if (_conn != value) { _conn = value; render(); } } } private string? _image_key = null; public string? image_key { get { return _image_key; } set { if (_image_key != value) { _image_key = value; render(); } } } public Plac.Image.ScalingMethod scaling { get; set; default = FIT; } private Gtk.Picture picture = new Gtk.Picture(); public Artwork() { Object(); } public Artwork.new_with_size(uint16 width, uint16 height) { Object(width: width, height: height); } construct { picture.set_parent(this); picture.halign = CENTER; picture.valign = CENTER; picture.width_request = _width; picture.height_request = _height; picture.content_fit = SCALE_DOWN; picture.add_css_class("plac-playback-artwork"); render(); } public void render() { picture.paintable = null; if (_conn == null || _image_key == null) { return; } var opts = new Plac.Image.GetOptions(); opts.set_size(scaling, _width, _height); opts.set_content_type(JPEG); _conn.get_image.begin(_image_key, opts, (obj, res) => { var result = _conn.get_image.end(res); if (result == null) { GLib.log("Plac", LEVEL_WARNING, "Failed to download image: Out of memory"); return; } if (result.code != OK) { GLib.log("Plac", LEVEL_WARNING, "Failed to download image: %s", result.code.to_string()); return; } if (result.image == null) { GLib.log("Plac", LEVEL_WARNING, "Failed to download image: image_field_missing"); return; } var bytes = GLib.Bytes.new_with_owner(result.image.data, result.image); try { var texture = Gdk.Texture.from_bytes(bytes); picture.paintable = texture; } catch (GLib.Error error) { GLib.log("Plac", LEVEL_WARNING, "Failed to generate artwork texture: %s", error.message); } }); } } }
-
-
-
@@ -49,6 +49,9 @@[GtkChild] private unowned Gtk.ListBox zone_list; [GtkChild] private unowned Artwork artwork; private Gee.HashMap<string, ZoneSelectorRow>zone_rows = new Gee.HashMap<string, ZoneSelectorRow>(); private string? zone_id = null;
-
@@ -88,6 +91,8 @@ this.render();} public void listen(Plac.AsyncConnection conn) { artwork.conn = conn; play.clicked.connect(() => { var row = zone_rows[zone_id]; if (row == null) {
-
@@ -227,9 +232,11 @@ playing_line1.label = zone.now_playing.two_line_line1;playing_line2.label = zone.now_playing.two_line_line2 != null ? zone.now_playing.two_line_line2 : ""; artwork.image_key = zone.now_playing.image_key; } else { playing_line1.label = ""; playing_line2.label = ""; artwork.image_key = null; } if (
-
-
-
@@ -42,6 +42,7 @@private string server_id; public MainWindow(Gtk.Application app, Plac.Discovery.Server server) { (typeof (Artwork)).ensure(); (typeof (ServerConnecting)).ensure(); (typeof (PlaybackToolbar)).ensure();
-
@@ -52,6 +53,7 @@ this.server_id = server.id;} public MainWindow.from_server_id(Gtk.Application app, string server_id) { (typeof (Artwork)).ensure(); (typeof (ServerConnecting)).ensure(); (typeof (PlaybackToolbar)).ensure();
-
@@ -60,6 +62,12 @@ this.server_id = server_id;} construct { var provider = new Gtk.CssProvider(); provider.load_from_resource("/jp/pocka/plac/gtk-adwaita/css/main-window.css"); Gtk.StyleContext.add_provider_for_display( this.display, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ); error_banner.button_clicked.connect(() => { try_listen(); });
-