Skip to content

Commit e0fe27b

Browse files
author
werwolf2303
committed
Feature implementation
Implemented fullscreen mode
1 parent e249300 commit e0fe27b

File tree

6 files changed

+316
-18
lines changed

6 files changed

+316
-18
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.spotifyxp.dialogs.FullscreenPlayerDialog">
3+
<grid id="27dc6" binding="contentPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
4+
<constraints>
5+
<xy x="20" y="20" width="500" height="400"/>
6+
</constraints>
7+
<properties/>
8+
<border type="none"/>
9+
<children>
10+
<grid id="43797" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
11+
<margin top="0" left="0" bottom="0" right="0"/>
12+
<constraints border-constraint="South"/>
13+
<properties>
14+
<maximumSize width="720" height="2147483647"/>
15+
</properties>
16+
<border type="none"/>
17+
<children>
18+
<grid id="b4462" binding="controlsPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
19+
<constraints>
20+
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
21+
</constraints>
22+
<properties/>
23+
<border type="none"/>
24+
<children/>
25+
</grid>
26+
</children>
27+
</grid>
28+
<grid id="52261" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
29+
<margin top="0" left="0" bottom="0" right="0"/>
30+
<constraints border-constraint="North"/>
31+
<properties/>
32+
<border type="none"/>
33+
<children>
34+
<component id="53f10" class="javax.swing.JButton" binding="closeButton">
35+
<constraints>
36+
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
37+
</constraints>
38+
<properties>
39+
<text value="X"/>
40+
</properties>
41+
</component>
42+
<hspacer id="3d025">
43+
<constraints>
44+
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
45+
</constraints>
46+
</hspacer>
47+
</children>
48+
</grid>
49+
<grid id="37eed" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
50+
<margin top="0" left="0" bottom="0" right="0"/>
51+
<constraints border-constraint="Center"/>
52+
<properties/>
53+
<border type="none"/>
54+
<children>
55+
<component id="58b3c" class="com.spotifyxp.swingextension.JImagePanel" binding="image">
56+
<constraints>
57+
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
58+
</constraints>
59+
<properties/>
60+
</component>
61+
<component id="25b59" class="javax.swing.JLabel" binding="playerTitleDesc">
62+
<constraints>
63+
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="2" fill="1" indent="5" use-parent-layout="false"/>
64+
</constraints>
65+
<properties>
66+
<font size="20"/>
67+
<text value="Label"/>
68+
</properties>
69+
</component>
70+
</children>
71+
</grid>
72+
</children>
73+
</grid>
74+
</form>
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/*
2+
* Copyright [2025] [Gianluca Beil]
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.spotifyxp.dialogs;
18+
19+
import com.intellij.uiDesigner.core.GridConstraints;
20+
import com.intellij.uiDesigner.core.GridLayoutManager;
21+
import com.intellij.uiDesigner.core.Spacer;
22+
import com.spotifyxp.PublicValues;
23+
import com.spotifyxp.deps.se.michaelthelin.spotify.model_objects.specification.Episode;
24+
import com.spotifyxp.deps.se.michaelthelin.spotify.model_objects.specification.Track;
25+
import com.spotifyxp.events.EventSubscriber;
26+
import com.spotifyxp.events.Events;
27+
import com.spotifyxp.events.SpotifyXPEvents;
28+
import com.spotifyxp.logging.ConsoleLogging;
29+
import com.spotifyxp.manager.InstanceManager;
30+
import com.spotifyxp.panels.ContentPanel;
31+
import com.spotifyxp.panels.PlayerArea;
32+
import com.spotifyxp.swingextension.JFrame;
33+
import com.spotifyxp.swingextension.JImagePanel;
34+
import com.spotifyxp.theming.themes.DarkGreen;
35+
import com.spotifyxp.utils.ApplicationUtils;
36+
import com.spotifyxp.utils.SpotifyUtils;
37+
import com.spotifyxp.utils.TrackUtils;
38+
import com.spotifyxp.utils.Utils;
39+
40+
import javax.swing.*;
41+
import javax.swing.plaf.FontUIResource;
42+
import javax.swing.text.StyleContext;
43+
import java.awt.*;
44+
import java.awt.event.*;
45+
import java.io.File;
46+
import java.net.MalformedURLException;
47+
import java.net.URL;
48+
import java.util.Locale;
49+
50+
public class FullscreenPlayerDialog {
51+
public JPanel contentPanel;
52+
public JImagePanel image;
53+
public JButton closeButton;
54+
public JPanel controlsPanel;
55+
public JLabel playerTitleDesc;
56+
public JFrame frame;
57+
58+
private int x, y, width, height;
59+
60+
public FullscreenPlayerDialog() {
61+
$$$setupUI$$$();
62+
63+
x = ContentPanel.playerArea.getX();
64+
y = ContentPanel.playerArea.getY();
65+
width = ContentPanel.playerArea.getWidth();
66+
height = ContentPanel.playerArea.getHeight();
67+
68+
controlsPanel.setLayout(null);
69+
70+
PublicValues.contentPanel.remove(ContentPanel.playerArea);
71+
72+
int centerX = (ContentPanel.playerArea.getWidth() - PlayerArea.playerPlayTime.getX()) / 2;
73+
centerX = centerX * -1;
74+
75+
ContentPanel.playerArea.setBounds(centerX + 40, 0, width, height);
76+
77+
controlsPanel.setPreferredSize(new Dimension(width, height));
78+
79+
controlsPanel.add(ContentPanel.playerArea);
80+
PublicValues.contentPanel.revalidate();
81+
PublicValues.contentPanel.repaint();
82+
83+
PlayerArea.playerImage.setVisible(false);
84+
PlayerArea.playerTitle.setVisible(false);
85+
PlayerArea.playerDescription.setVisible(false);
86+
87+
closeButton.addActionListener(new ActionListener() {
88+
@Override
89+
public void actionPerformed(ActionEvent e) {
90+
frame.dispose();
91+
for (WindowListener listener : frame.getWindowListeners()) {
92+
listener.windowClosing(null);
93+
}
94+
}
95+
});
96+
97+
Events.subscribe(SpotifyXPEvents.trackNext.getName(), new EventSubscriber() {
98+
@Override
99+
public void run(Object... data) {
100+
if (data[0] instanceof Track) {
101+
playerTitleDesc.setText(((Track) data[0]).getName() + " - " + TrackUtils.getArtists(((Track) data[0]).getArtists()));
102+
try {
103+
image.setImage(new URL(SpotifyUtils.getImageForSystem(((Track) data[0]).getAlbum().getImages()).getUrl()));
104+
} catch (MalformedURLException e) {
105+
ConsoleLogging.Throwable(e);
106+
}
107+
} else if (data[0] instanceof Episode) {
108+
playerTitleDesc.setText(((Episode) data[0]).getShow().getName() + " - " + ((Episode) data[0]).getName());
109+
try {
110+
image.setImage(new URL(SpotifyUtils.getImageForSystem(((Episode) data[0]).getImages()).getUrl()));
111+
} catch (MalformedURLException e) {
112+
ConsoleLogging.Throwable(e);
113+
}
114+
}
115+
}
116+
});
117+
118+
if (InstanceManager.getSpotifyPlayer().currentPlayable() != null) {
119+
if (InstanceManager.getSpotifyPlayer().currentPlayable().toSpotifyUri().split(":")[1].equals("track")) {
120+
playerTitleDesc.setText(PlayerArea.playerTitle.getText() + " - " + PlayerArea.playerDescription.getText());
121+
} else {
122+
playerTitleDesc.setText(PlayerArea.playerDescription + " - " + PlayerArea.playerTitle.getText());
123+
}
124+
125+
image.setImage(PlayerArea.playerImage.getImageStream());
126+
}
127+
}
128+
129+
public void open() {
130+
frame = new JFrame(ApplicationUtils.getName() + " - Fullscreen Player");
131+
frame.setContentPane(contentPanel);
132+
frame.setUndecorated(true);
133+
frame.setAlwaysOnTop(true);
134+
frame.addWindowListener(new WindowAdapter() {
135+
@Override
136+
public void windowClosing(WindowEvent e) {
137+
controlsPanel.remove(ContentPanel.playerArea);
138+
ContentPanel.playerArea.setBounds(x, y, width, height);
139+
PublicValues.contentPanel.add(ContentPanel.playerArea);
140+
PublicValues.contentPanel.revalidate();
141+
PublicValues.contentPanel.repaint();
142+
143+
PlayerArea.playerImage.setVisible(true);
144+
PlayerArea.playerTitle.setVisible(true);
145+
PlayerArea.playerDescription.setVisible(true);
146+
}
147+
});
148+
frame.pack();
149+
frame.setVisible(true);
150+
Utils.moveToScreen(frame, Utils.getDisplayNumber(ContentPanel.frame));
151+
ContentPanel.frame.getGraphicsConfiguration().getDevice().setFullScreenWindow(frame);
152+
}
153+
154+
public void close() {
155+
frame.dispose();
156+
}
157+
158+
public static void main(String[] args) {
159+
PublicValues.theme = new DarkGreen();
160+
PublicValues.theme.initTheme();
161+
ContentPanel.frame.setBackground(Color.decode("#3c3f41"));
162+
FullscreenPlayerDialog dialog = new FullscreenPlayerDialog();
163+
dialog.open();
164+
}
165+
166+
/**
167+
* Method generated by IntelliJ IDEA GUI Designer
168+
* >>> IMPORTANT!! <<<
169+
* DO NOT edit this method OR call it in your code!
170+
*
171+
* @noinspection ALL
172+
*/
173+
private void $$$setupUI$$$() {
174+
contentPanel = new JPanel();
175+
contentPanel.setLayout(new BorderLayout(0, 0));
176+
final JPanel panel1 = new JPanel();
177+
panel1.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
178+
panel1.setMaximumSize(new Dimension(720, 2147483647));
179+
contentPanel.add(panel1, BorderLayout.SOUTH);
180+
controlsPanel = new JPanel();
181+
controlsPanel.setLayout(new BorderLayout(0, 0));
182+
panel1.add(controlsPanel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
183+
final JPanel panel2 = new JPanel();
184+
panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));
185+
contentPanel.add(panel2, BorderLayout.NORTH);
186+
closeButton = new JButton();
187+
closeButton.setText("X");
188+
panel2.add(closeButton, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
189+
final Spacer spacer1 = new Spacer();
190+
panel2.add(spacer1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));
191+
final JPanel panel3 = new JPanel();
192+
panel3.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
193+
contentPanel.add(panel3, BorderLayout.CENTER);
194+
image = new JImagePanel();
195+
panel3.add(image, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
196+
playerTitleDesc = new JLabel();
197+
Font playerTitleDescFont = this.$$$getFont$$$(null, -1, 20, playerTitleDesc.getFont());
198+
if (playerTitleDescFont != null) playerTitleDesc.setFont(playerTitleDescFont);
199+
playerTitleDesc.setText("Label");
200+
panel3.add(playerTitleDesc, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_SOUTH, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 5, false));
201+
}
202+
203+
/**
204+
* @noinspection ALL
205+
*/
206+
private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) {
207+
if (currentFont == null) return null;
208+
String resultName;
209+
if (fontName == null) {
210+
resultName = currentFont.getName();
211+
} else {
212+
Font testFont = new Font(fontName, Font.PLAIN, 10);
213+
if (testFont.canDisplay('a') && testFont.canDisplay('1')) {
214+
resultName = fontName;
215+
} else {
216+
resultName = currentFont.getName();
217+
}
218+
}
219+
Font font = new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize());
220+
boolean isMac = System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).startsWith("mac");
221+
Font fontWithFallback = isMac ? new Font(font.getFamily(), font.getStyle(), font.getSize()) : new StyleContext().getFont(font.getFamily(), font.getStyle(), font.getSize());
222+
return fontWithFallback instanceof FontUIResource ? fontWithFallback : new FontUIResource(fontWithFallback);
223+
}
224+
225+
/**
226+
* @noinspection ALL
227+
*/
228+
public JComponent $$$getRootComponent$$$() {
229+
return contentPanel;
230+
}
231+
232+
}

src/main/java/com/spotifyxp/listeners/PlayerListener.java

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -128,24 +128,7 @@ public void onTrackChanged(@NotNull Player player, @NotNull PlayableId playableI
128128
}
129129
break;
130130
default:
131-
ConsoleLogging.warning(PublicValues.language.translate("playerlistener.playableid.unknowntype"));
132-
Track t = InstanceManager.getSpotifyApi().getTrack(playableId.toSpotifyUri().split(":")[2]).build().execute();
133-
Events.triggerEvent(SpotifyXPEvents.trackNext.getName(), t);
134-
PlayerArea.playerPlayTimeTotal.setText(String.valueOf(t.getDurationMs()));
135-
PlayerArea.playerTitle.setText(t.getName());
136-
for (ArtistSimplified artist : t.getArtists()) {
137-
if (artists.toString().isEmpty()) {
138-
artists.append(artist.getName());
139-
} else {
140-
artists.append(", ").append(artist.getName());
141-
}
142-
}
143-
try {
144-
PlayerArea.playerImage.setImage(new URL(SpotifyUtils.getImageForSystem(t.getAlbum().getImages()).getUrl()).openStream());
145-
} catch (Exception e) {
146-
ConsoleLogging.warning("Failed to load cover for track");
147-
PlayerArea.playerImage.setImage(SVGUtils.svgToImageInputStreamSameSize(Graphics.NOTHINGPLAYING.getInputStream(), PlayerArea.playerImage.getSize()));
148-
}
131+
ConsoleLogging.Throwable(new RuntimeException("Unhandled/Unknown uri type: " + playableId.toSpotifyUri().split(":")[1]));
149132
}
150133
PlayerArea.playerDescription.setText(artists.toString());
151134
} catch (IOException | JSONException e) {

src/main/java/com/spotifyxp/panels/PlayerArea.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.spotifyxp.PublicValues;
1919
import com.spotifyxp.ctxmenu.ContextMenu;
2020
import com.spotifyxp.deps.com.spotify.context.ContextTrackOuterClass;
21+
import com.spotifyxp.dialogs.FullscreenPlayerDialog;
2122
import com.spotifyxp.dialogs.LyricsDialog;
2223
import com.spotifyxp.events.EventSubscriber;
2324
import com.spotifyxp.events.Events;
@@ -426,6 +427,12 @@ public void run() {
426427
pipPlayer.open();
427428
}
428429
});
430+
contextMenu.addItem(PublicValues.language.translate("ui.playerarea.ctxmenu.item2"), new Runnable() {
431+
@Override
432+
public void run() {
433+
new FullscreenPlayerDialog().open();
434+
}
435+
});
429436

430437
addMouseListener(new MouseAdapter() {
431438
@Override

src/main/java/com/spotifyxp/panels/Queue.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ public void mouseClicked(MouseEvent e) {
132132
((DefaultListModel<?>) queueList.getModel()).clear();
133133
try {
134134
for (ContextTrackOuterClass.ContextTrack t : InstanceManager.getSpotifyPlayer().tracks(true).next) {
135+
// Metadata map??
135136
Track track = InstanceManager.getSpotifyApi().getTrack(t.getUri().split(":")[2]).build().execute();
136137
queueUriCache.add(t.getUri());
137138
String a = TrackUtils.getArtists(track.getArtists());

src/main/resources/lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
"ui.search.songlist.length": "Length",
102102
"ui.search.songlist.songname": "Name",
103103
"ui.playerarea.ctxmenu.item1": "Open in PiP",
104+
"ui.playerarea.ctxmenu.item2": "Switch to Fullscreen Mode",
104105
"ui.settings.browser": "Browser",
105106
"settings.mypal.path": "Mypal executable path",
106107
"ui.settings.playback.label": "Playback",

0 commit comments

Comments
 (0)