diff --git a/README.md b/README.md index d78be41..e796743 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,6 @@ Install from source ## Upcoming features -- Support for video players -- Animated text -- Desaturated player icon -- Clickable title with actions \ No newline at end of file +- ~Support for video players~ [DONE] +- ~Desaturated player icon~ [DONE] +- ~Clickable title with actions~ [DONE] \ No newline at end of file diff --git a/dbus.js b/dbus.js index 60c4c5e..5c72fa4 100644 --- a/dbus.js +++ b/dbus.js @@ -99,8 +99,9 @@ const getMetadata = (player) => { .recursiveUnpack()[0]; let title = metaData["xesam:title"]; let artist = metaData["xesam:artist"]; + let id = metaData["mpris:trackid"]; // log("Got metadata"); - resolve([title, artist]); + resolve([title, artist, id]); } catch (e) { reject(e); } diff --git a/extension.js b/extension.js index c2c934c..00fcf33 100644 --- a/extension.js +++ b/extension.js @@ -18,7 +18,7 @@ let maxDisplayLength, extensionPosition, extensionIndex, coloredPlayerIcon, - animateText, + showAllOnHover, mouseActionsLeftClick, mouseActionsRightClick; @@ -33,9 +33,11 @@ let onUpdateDelayChanged, onExtensionPositionChanged, onExtensionIndexChanged, onColoredPlayerIconChanged, - onAnimateTextChanged, + onSepCharStartChanged, + onSepCharEndChanged, onMouseActionsLeftClickChanged, - onMouseActionsRightClickChanged; + onMouseActionsRightClickChanged, + onShowAllOnHoverChanged; let mainLoop; let settings; @@ -46,7 +48,7 @@ let lastPlayer, lastState, lastPlayerChanged, lastStateChanged, - animateTextCount, + mouseHovered, contentRemoved; // Constants @@ -57,6 +59,7 @@ const playerIcons = { firefox: "firefox", rhythmbox: "rhythmbox", spotify: "spotify", + vlc: "vlc", }; const positions = { @@ -76,7 +79,6 @@ let buttonNext, iconPlay, iconPrev, iconPlayer, - labelText, labelSeperatorStart, labelSeperatorEnd; @@ -134,25 +136,11 @@ const updatePlayerIconEffects = () => { } }; -const scrollText = () => { - // let difference = lastMetadata.length - maxDisplayLength; - // log("Animating text", difference, animateTextCount); - // if (difference <= 0) { - // displayText = lastMetadata; - // } - // displayText = lastMetadata.substr(animateTextCount, difference); - // if (displayText.length < maxDisplayLength) { - // displayText = - // displayText + " ".repeat(maxDisplayLength - displayText.length); - // } - // animateTextCount++; -}; - // Housekeeping methods const addContent = () => { // let currentIndex; - log(`Adding to ${extensionPosition} box`); + // log(`Adding to ${extensionPosition} box`); let currentIndex = 0; if (!hidePlayerIcon) { Main.panel[positions[extensionPosition]].insert_child_at_index( @@ -200,24 +188,31 @@ const addContent = () => { const removeContent = () => { // extensionPosition, extensionPosition Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah - log(`Removing from ${extensionPosition} box`); + // log(`Removing from ${extensionPosition} box`); Main.panel[positions[extensionPosition]].remove_actor(buttonNext); Main.panel[positions[extensionPosition]].remove_actor(buttonToggle); Main.panel[positions[extensionPosition]].remove_actor(buttonPrev); Main.panel[positions[extensionPosition]].remove_actor(buttonLabel); + Main.panel[positions[extensionPosition]].remove_actor(labelSeperatorEnd); + Main.panel[positions[extensionPosition]].remove_actor(labelSeperatorStart); Main.panel[positions[extensionPosition]].remove_actor(iconPlayer); }; // Utility methods const updateData = (player, _playerState, _title, _artist) => { + // log(mouseHovered, showAllOnHover); + let currentMetadata = `${_title || ""}${_artist ? " - " + _artist : ""}`; + // log(currentMetadata); + let splittedPlayer = player.split("."); + // log(splittedPlayer, player); if (lastPlayer !== player) { - log("Updating player"); + // log("Updating player"); currentPlayer = player; lastPlayer = player; playerIcon = playerIcons["default"]; for ([key, value] of Object.entries(playerIcons)) { - if (player.includes(key)) { + if (splittedPlayer.includes(key)) { playerIcon = playerIcons[key]; break; } @@ -225,48 +220,54 @@ const updateData = (player, _playerState, _title, _artist) => { lastPlayerChanged = true; } if (lastState !== _playerState) { - log("Updating State"); + // log("Updating State"); playerState = _playerState; lastState = _playerState; lastStateChanged = true; } - if (_title + _artist !== lastMetadata) { - log("Updating Metadata"); - lastMetadata = _title + _artist; - animateTextCount = 0; - displayText = `${_title}${_artist ? " - " + _artist : ""}`; + if (currentMetadata === "" && splittedPlayer.includes("vlc")) { + currentMetadata = "Playing video"; + } + if (currentMetadata !== lastMetadata) { + // log("Updating Metadata"); + lastMetadata = currentMetadata; + displayText = currentMetadata; } if (lastMetadata.length > maxDisplayLength) { - log("Trimming text"); - // if (animateText) { - // scrollText(); - // } else { - // } - displayText = displayText.substring(0, maxDisplayLength - 3) + "..."; + // log("Trimming text", lastMetadata.length, maxDisplayLength); + if (mouseHovered && showAllOnHover) { + displayText = lastMetadata; + } else { + displayText = + lastMetadata.substring(0, maxDisplayLength - 3) + "..."; + } + } else { + displayText = lastMetadata; } }; const updateMetadata = async () => { try { - // log("Determining current player"); + // // log("Determining current player"); playersList = await getPlayers(); if (playersList.length > 0) { let playerStateMap = []; let playerDataMap = {}; - // log("Starting for loop"); - // log(`Player list - ${playersList}`); + // // log("Starting for loop"); + // // log(`Player list - ${playersList}`); for (let i = 0; i <= playersList.length; i++) { player = playersList[i]; if (player) { _playerStatePromise = getPlaybackStatus(player); _metadataPromise = getMetadata(player); - // log("Resolving promises"); - [_playerState, [_title, _artist]] = await Promise.all([ + // // log("Resolving promises"); + [_playerState, [_title, _artist, _id]] = await Promise.all([ _playerStatePromise, _metadataPromise, ]); - // log("Promises resolved"); - if (_title) { + // log(_id); + // // log("Promises resolved"); + if (_id) { playerStateMap.push([player, _playerState]); playerDataMap[player] = { _title, @@ -277,14 +278,14 @@ const updateMetadata = async () => { } } - // log(`${playerStateMap.length} eligible players found!`); + // // log(`${playerStateMap.length} eligible players found!`); let playingPlayers = playerStateMap.filter(([player, state]) => { if (state === "Playing") { return true; } return false; }); - // log(`${playingPlayers.length} playing players found!`); + // // log(`${playingPlayers.length} playing players found!`); if (playingPlayers.length > 0) { if (contentRemoved) { addContent(); @@ -324,7 +325,7 @@ const updateMetadata = async () => { const updateContent = () => { if (lastStateChanged) { - log("Updating state icon"); + // log("Updating state icon"); if (playerState === "Playing") { buttonToggle.set_child(iconPause); } else { @@ -333,11 +334,23 @@ const updateContent = () => { lastStateChanged = false; } if (lastPlayerChanged) { - log("Updating player icon"); + // log("Updating player icon"); iconPlayer.set_icon_name(playerIcon); lastPlayerChanged = false; } - labelText.set_text(`${displayText}`); + buttonLabel.set_label(`${displayText}`); +}; + +const startMainLoop = () => { + mainLoop = Mainloop.timeout_add(updateDelay, () => { + (async () => { + startTime = Date.now(); + await updateMetadata(); + updateContent(); + // log(`Took ${Date.now() - startTime} milliseconds`); + })(); + return true; + }); }; // Lifecycle methods @@ -352,21 +365,12 @@ const enable = () => { onUpdateDelayChanged = settings.connect("changed::update-delay", () => { updateDelay = settings.get_int("update-delay"); Mainloop.source_remove(mainLoop); - mainLoop = Mainloop.timeout_add(updateDelay, () => { - updatePlayers(); - updateMetadata(); - updateContent(); - return true; - }); - // log(`Updated setting "updateDelay": ${updateDelay}`); + startMainLoop(); + // // log(`Updated setting "updateDelay": ${updateDelay}`); }); onMaxLengthChanged = settings.connect("changed::max-text-length", () => { maxDisplayLength = settings.get_int("max-text-length"); - // buttonLabel.set_style( - // `width: ${maxDisplayLength}px;text-overflow: clip;` - // ); - // log(`Updated setting "maxDisplayLength": ${maxDisplayLength}`); }); onHideTrackNameChanged = settings.connect("changed::hide-text", () => { @@ -433,9 +437,30 @@ const enable = () => { } ); - onAnimateTextChanged = settings.connect("changed::animate-text", () => { - animateText = settings.get_boolean("animate-text"); - }); + onShowAllOnHoverChanged = settings.connect( + "changed::show-all-on-hover", + () => { + showAllOnHover = settings.get_boolean("show-all-on-hover"); + } + ); + + onSepCharStartChanged = settings.connect( + "changed::seperator-char-start", + () => { + labelSeperatorStart.set_text( + settings.get_string("seperator-char-start") + ); + } + ); + + onSepCharEndChanged = settings.connect( + "changed::seperator-char-end", + () => { + labelSeperatorEnd.set_text( + settings.get_string("seperator-char-end") + ); + } + ); updateDelay = settings.get_int("update-delay"); maxDisplayLength = settings.get_int("max-text-length"); @@ -445,14 +470,17 @@ const enable = () => { extensionIndex = settings.get_int("extension-index"); extensionPosition = settings.get_string("extension-position"); coloredPlayerIcon = settings.get_boolean("colored-player-icon"); - animateText = settings.get_boolean("animate-text"); + showAllOnHover = settings.get_boolean("show-all-on-hover"); // UI Elements buttonToggle = new St.Button({ style_class: "panel-button" }); buttonNext = new St.Button({ style_class: "panel-button" }); buttonPrev = new St.Button({ style_class: "panel-button" }); - buttonLabel = new St.Button({ track_hover: false }); + buttonLabel = new St.Button({ + track_hover: false, + label: "No player found", + }); iconPlay = new St.Icon({ icon_name: "media-playback-start-symbolic", @@ -476,20 +504,13 @@ const enable = () => { style: "padding-right: 5px;", }); - labelText = new St.Label({ - text: "No player found", - y_align: Clutter.ActorAlign.CENTER, - }); - labelSeperatorStart = new St.Label({ - text: "|", - style: "padding-right: 2px;", + style: "padding-right: 4px;", y_align: Clutter.ActorAlign.CENTER, }); labelSeperatorEnd = new St.Label({ - text: "|", - style: "padding-left: 2px;", + style: "padding-left: 4px;", y_align: Clutter.ActorAlign.CENTER, }); @@ -503,8 +524,16 @@ const enable = () => { buttonToggle.set_child(iconPlay); buttonToggle.connect("button-release-event", _actionToggle); - buttonLabel.set_child(labelText); buttonLabel.connect("button-release-event", _mouseAction); + buttonLabel.connect("enter-event", () => { + mouseHovered = true; + }); + buttonLabel.connect("leave-event", () => { + mouseHovered = false; + }); + + labelSeperatorStart.set_text(settings.get_string("seperator-char-start")); + labelSeperatorEnd.set_text(settings.get_string("seperator-char-end")); // buttonLabel.set_style(`width: ${maxDisplayLength}px;`); @@ -515,12 +544,7 @@ const enable = () => { addContent(); // Start the main loop - mainLoop = Mainloop.timeout_add(updateDelay, () => { - updateMetadata().then(() => { - updateContent(); - }); - return true; - }); + startMainLoop(); }; const disable = () => { @@ -536,7 +560,9 @@ const disable = () => { settings.disconnect(onMouseActionsLeftClickChanged); settings.disconnect(onMouseActionsRightClickChanged); settings.disconnect(onColoredPlayerIconChanged); - settings.disconnect(onAnimateTextChanged); + settings.disconnect(onSepCharEndChanged); + settings.disconnect(onSepCharStartChanged); + settings.disconnect(onShowAllOnHoverChanged); buttonNext.destroy(); buttonPrev.destroy(); @@ -545,7 +571,8 @@ const disable = () => { iconPause.destroy(); iconPlay.destroy(); iconPrev.destroy(); - labelText.destroy(); + labelSeperatorEnd.destroy(); + labelSeperatorStart.destroy(); buttonLabel.destroy(); removeContent(); diff --git a/metadata.json b/metadata.json index 0ea47cf..692c560 100644 --- a/metadata.json +++ b/metadata.json @@ -4,6 +4,6 @@ "uuid": "mediacontrols@cliffniff.github.com", "url": "https://github.com/cliffniff/media-controls", "settings-schema": "org.gnome.shell.extensions.mediacontrols", - "version": 0.4, + "version": 4, "shell-version": ["3.38", "40"] } diff --git a/prefs.js b/prefs.js index 3544e0b..2667f95 100644 --- a/prefs.js +++ b/prefs.js @@ -15,20 +15,37 @@ const shellVersion = Number.parseInt(major); const positions = ["left", "center", "right"]; const mouseActions = ["none", "toggle_play", "play", "pause", "next", "prev"]; +const sepChars = [ + "|...|", + "[...]", + "(...)", + "{...}", + "/...\\", + "\\.../", + ":...:", + "-...-", + "_..._", + "=...=", + "•...•", + "█...█", +]; function init() {} function buildPrefsWidget() { let settings = ExtensionUtils.getSettings(); - + let scrolledWindow = new Gtk.ScrolledWindow({ + max_content_height: 600, + }); let widgetPrefs; if (shellVersion < 40) { widgetPrefs = new Gtk.Grid({ margin: 15, - column_spacing: 12, - row_spacing: 12, + column_spacing: 8, + row_spacing: 8, visible: true, column_homogeneous: true, + vexpand: true, }); } else { widgetPrefs = new Gtk.Grid({ @@ -36,10 +53,11 @@ function buildPrefsWidget() { margin_bottom: 15, margin_start: 15, margin_end: 15, - column_spacing: 12, - row_spacing: 12, + column_spacing: 8, + row_spacing: 8, visible: true, column_homogeneous: true, + vexpand: true, }); } @@ -84,9 +102,9 @@ function buildPrefsWidget() { let entryUpdateDelay = new Gtk.SpinButton({ adjustment: new Gtk.Adjustment({ - lower: 500, - upper: 5000, - step_increment: 500, + lower: 0, + upper: 10000, + step_increment: 100, }), visible: true, }); @@ -176,21 +194,73 @@ function buildPrefsWidget() { widgetPrefs.attach(switchColoredPlayerIcon, 1, index, 1, 1); /* Hide controls */ - let labelAnimateText = new Gtk.Label({ - label: "Animate text:", + let labelShowAllOnHover = new Gtk.Label({ + label: "Show hidden content on hover:", halign: Gtk.Align.START, visible: true, }); - let switchAnimateText = new Gtk.Switch({ + let switchShowAllOnHover = new Gtk.Switch({ valign: Gtk.Align.END, halign: Gtk.Align.END, visible: true, }); index++; - widgetPrefs.attach(labelAnimateText, 0, index, 1, 1); - widgetPrefs.attach(switchAnimateText, 1, index, 1, 1); + widgetPrefs.attach(labelShowAllOnHover, 0, index, 1, 1); + widgetPrefs.attach(switchShowAllOnHover, 1, index, 1, 1); + + /* Change seperator character */ + let labelSeperatorChar = new Gtk.Label({ + label: "Change seperator characters:", + halign: Gtk.Align.START, + visible: true, + }); + + let labelSepCharPresets = new Gtk.Label({ + label: "Presets:", + halign: Gtk.Align.END, + visible: true, + }); + + let labelSepCharCustom = new Gtk.Label({ + label: "Custom:", + halign: Gtk.Align.END, + visible: true, + }); + + let comboboxSepCharPresets = new Gtk.ComboBoxText({ + halign: Gtk.Align.END, + visible: true, + }); + + let entrySepCharCustom = new Gtk.Entry({ + halign: Gtk.Align.END, + buffer: new Gtk.EntryBuffer(), + placeholder_text: "Ex - '<...>'", + }); + + for (let i = 0; i < sepChars.length; i++) { + comboboxSepCharPresets.append(sepChars[i], sepChars[i]); + } + + comboboxSepCharPresets.set_active( + sepChars.indexOf( + settings.get_string("seperator-char-start") + + "..." + + settings.get_string("seperator-char-end") + ) + ); + + index++; + widgetPrefs.attach(labelSeperatorChar, 0, index, 1, 1); + widgetPrefs.attach(labelSepCharPresets, 1, index, 1, 1); + index++; + widgetPrefs.attach(comboboxSepCharPresets, 1, index, 1, 1); + index++; + widgetPrefs.attach(labelSepCharCustom, 1, index, 1, 1); + index++; + widgetPrefs.attach(entrySepCharCustom, 1, index, 1, 1); // SECTION END @@ -273,14 +343,12 @@ function buildPrefsWidget() { label: "Left click:", halign: Gtk.Align.END, visible: true, - margin_end: 130, }); let labelMouseActionsRightClick = new Gtk.Label({ label: "Right click:", halign: Gtk.Align.END, visible: true, - margin_end: 130, }); let comboboxMouseActionsLeftClick = new Gtk.ComboBoxText({ @@ -309,9 +377,11 @@ function buildPrefsWidget() { index++; widgetPrefs.attach(labelMouseActions, 0, index, 1, 1); widgetPrefs.attach(labelMouseActionsLeftClick, 1, index, 1, 1); + index++; widgetPrefs.attach(comboboxMouseActionsLeftClick, 1, index, 1, 1); index++; widgetPrefs.attach(labelMouseActionsRightClick, 1, index, 1, 1); + index++; widgetPrefs.attach(comboboxMouseActionsRightClick, 1, index, 1, 1); // SECTION END @@ -354,8 +424,8 @@ function buildPrefsWidget() { Gio.SettingsBindFlags.DEFAULT ); settings.bind( - "animate-text", - switchAnimateText, + "show-all-on-hover", + switchShowAllOnHover, "active", Gio.SettingsBindFlags.DEFAULT ); @@ -372,6 +442,30 @@ function buildPrefsWidget() { Gio.SettingsBindFlags.DEFAULT ); + settings.bind( + "seperator-char-start", + comboboxSepCharPresets, + "value", + Gio.SettingsBindFlags.DEFAULT + ); + + comboboxSepCharPresets.connect("changed", (widget) => { + let presetValue = sepChars[widget.get_active()]; + settings.set_string("seperator-char-start", presetValue.charAt(0)); + settings.set_string( + "seperator-char-end", + presetValue.charAt(presetValue.length - 1) + ); + }); + + entrySepCharCustom.connect("changed", (widget) => { + let customValues = widget.get_text().split("..."); + if (customValues[0] && customValues[1]) { + settings.set_string("seperator-char-start", customValues[0]); + settings.set_string("seperator-char-end", customValues[1]); + } + }); + comboboxMouseActionsLeftClick.connect("changed", (widget) => { settings.set_string( "mouse-actions-left", @@ -385,5 +479,6 @@ function buildPrefsWidget() { mouseActions[widget.get_active()] ); }); - return widgetPrefs; + scrolledWindow.set_child(widgetPrefs); + return scrolledWindow; } diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index c6c1529..4eb81dc 100644 Binary files a/schemas/gschemas.compiled and b/schemas/gschemas.compiled differ diff --git a/schemas/org.gnome.shell.extensions.mediacontrols.gschema.xml b/schemas/org.gnome.shell.extensions.mediacontrols.gschema.xml index 8c021c6..8b3a8b9 100644 --- a/schemas/org.gnome.shell.extensions.mediacontrols.gschema.xml +++ b/schemas/org.gnome.shell.extensions.mediacontrols.gschema.xml @@ -5,7 +5,7 @@ 40 - 1000 + 100 false @@ -19,10 +19,19 @@ false - + true + + '|' + + + + + '|' + + 'none'