mirror of
https://github.com/yattee/yattee.git
synced 2024-12-13 05:40:32 +05:30
Minor player controls improvements (fix #94)
This commit is contained in:
parent
45d591537c
commit
e3cf77e928
@ -241,7 +241,11 @@ final class MPVBackend: PlayerBackend {
|
||||
client?.setDoubleAsync("speed", Double(rate))
|
||||
}
|
||||
|
||||
func closeItem() {}
|
||||
func closeItem() {
|
||||
handleEOF = false
|
||||
client?.pause()
|
||||
client?.stop()
|
||||
}
|
||||
|
||||
func enterFullScreen() {}
|
||||
|
||||
|
@ -371,7 +371,11 @@ final class PlayerModel: ObservableObject {
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
|
||||
self?.upgradeToStream(stream, force: true)
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.upgradeToStream(stream, force: true)
|
||||
self.setNeedsDrawing(self.presentingPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,6 +452,10 @@ final class PlayerModel: ObservableObject {
|
||||
|
||||
logger.info("exiting fullscreen")
|
||||
|
||||
if controls.playingFullscreen {
|
||||
toggleFullscreen(true)
|
||||
}
|
||||
|
||||
backend.exitFullScreen()
|
||||
}
|
||||
#endif
|
||||
|
@ -53,18 +53,21 @@ struct PlayerControls: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
timeline
|
||||
.offset(y: 10)
|
||||
.zIndex(1)
|
||||
Group {
|
||||
timeline
|
||||
.offset(y: 10)
|
||||
.zIndex(1)
|
||||
|
||||
bottomBar
|
||||
bottomBar
|
||||
|
||||
#if os(macOS)
|
||||
.background(VisualEffectBlur(material: .hudWindow))
|
||||
#elseif os(iOS)
|
||||
.background(VisualEffectBlur(blurStyle: .systemThinMaterial))
|
||||
#endif
|
||||
.mask(RoundedRectangle(cornerRadius: 3))
|
||||
#if os(macOS)
|
||||
.background(VisualEffectBlur(material: .hudWindow))
|
||||
#elseif os(iOS)
|
||||
.background(VisualEffectBlur(blurStyle: .systemThinMaterial))
|
||||
#endif
|
||||
.mask(RoundedRectangle(cornerRadius: 3))
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
}
|
||||
.opacity(model.presentingControls ? 1 : 0)
|
||||
@ -104,9 +107,6 @@ struct PlayerControls: View {
|
||||
|
||||
var statusBar: some View {
|
||||
HStack(spacing: 4) {
|
||||
#if os(iOS)
|
||||
hidePlayerButton
|
||||
#endif
|
||||
Text(playbackStatus)
|
||||
|
||||
Text("•")
|
||||
@ -129,11 +129,10 @@ struct PlayerControls: View {
|
||||
}
|
||||
|
||||
private var hidePlayerButton: some View {
|
||||
Button {
|
||||
button("Hide", systemImage: "chevron.down") {
|
||||
player.hide()
|
||||
} label: {
|
||||
Image(systemName: "chevron.down.circle.fill")
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
.keyboardShortcut(.cancelAction)
|
||||
#endif
|
||||
@ -170,14 +169,18 @@ struct PlayerControls: View {
|
||||
}
|
||||
|
||||
var buttonsBar: some View {
|
||||
HStack {
|
||||
HStack(spacing: 20) {
|
||||
#if !os(tvOS)
|
||||
hidePlayerButton
|
||||
|
||||
fullscreenButton
|
||||
#if os(iOS)
|
||||
pipButton
|
||||
#endif
|
||||
rateButton
|
||||
|
||||
closeVideoButton
|
||||
|
||||
Spacer()
|
||||
#endif
|
||||
// button("Music Mode", systemImage: "music.note")
|
||||
@ -225,6 +228,19 @@ struct PlayerControls: View {
|
||||
#endif
|
||||
}
|
||||
|
||||
private var closeVideoButton: some View {
|
||||
button("Close", systemImage: "xmark") {
|
||||
player.pause()
|
||||
|
||||
player.hide()
|
||||
player.closePiP()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||
player.closeCurrentItem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ratePicker: some View {
|
||||
Picker("Rate", selection: rateBinding) {
|
||||
ForEach(PlayerModel.availableRates, id: \.self) { rate in
|
||||
@ -244,6 +260,8 @@ struct PlayerControls: View {
|
||||
player.avPlayerBackend.switchToMPVOnPipClose = true
|
||||
}
|
||||
|
||||
player.exitFullScreen()
|
||||
|
||||
if player.activeBackend != PlayerBackendType.appleAVPlayer {
|
||||
player.saveTime {
|
||||
player.changeActiveBackend(from: .mpv, to: .appleAVPlayer)
|
||||
@ -261,7 +279,7 @@ struct PlayerControls: View {
|
||||
var mediumButtonsBar: some View {
|
||||
HStack {
|
||||
#if !os(tvOS)
|
||||
button("Seek Backward", systemImage: "gobackward.10", size: 50, cornerRadius: 10) {
|
||||
button("Seek Backward", systemImage: "gobackward.10", size: 30, cornerRadius: 5) {
|
||||
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
|
||||
}
|
||||
|
||||
@ -279,8 +297,7 @@ struct PlayerControls: View {
|
||||
button(
|
||||
model.isPlaying ? "Pause" : "Play",
|
||||
systemImage: model.isPlaying ? "pause.fill" : "play.fill",
|
||||
size: 50,
|
||||
cornerRadius: 10
|
||||
size: 30, cornerRadius: 5
|
||||
) {
|
||||
player.backend.togglePlay()
|
||||
}
|
||||
@ -295,7 +312,7 @@ struct PlayerControls: View {
|
||||
Spacer()
|
||||
|
||||
#if !os(tvOS)
|
||||
button("Seek Forward", systemImage: "goforward.10", size: 50, cornerRadius: 10) {
|
||||
button("Seek Forward", systemImage: "goforward.10", size: 30, cornerRadius: 5) {
|
||||
player.backend.seek(relative: .secondsInDefaultTimescale(10))
|
||||
}
|
||||
#if os(tvOS)
|
||||
@ -306,7 +323,7 @@ struct PlayerControls: View {
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
.font(.system(size: 30))
|
||||
.font(.system(size: 20))
|
||||
.padding(.horizontal, 4)
|
||||
}
|
||||
|
||||
@ -361,6 +378,25 @@ struct PlayerControls: View {
|
||||
|
||||
struct PlayerControls_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
PlayerControls(player: PlayerModel())
|
||||
let model = PlayerControlsModel()
|
||||
model.presentingControls = true
|
||||
model.currentTime = .secondsInDefaultTimescale(0)
|
||||
model.duration = .secondsInDefaultTimescale(120)
|
||||
|
||||
let view = ZStack {
|
||||
Color.red
|
||||
|
||||
PlayerControls(player: PlayerModel())
|
||||
.injectFixtureEnvironmentObjects()
|
||||
.environmentObject(model)
|
||||
}
|
||||
|
||||
return Group {
|
||||
if #available(iOS 15.0, *) {
|
||||
view.previewInterfaceOrientation(.landscapeLeft)
|
||||
} else {
|
||||
view
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ struct TimelineView: View {
|
||||
@State private var draggedFrom: Double = 0
|
||||
|
||||
private var start: Double = 0.0
|
||||
private var height = 10.0
|
||||
private var height = 8.0
|
||||
|
||||
var cornerRadius: Double
|
||||
var thumbTooltipWidth: Double = 100
|
||||
@ -26,26 +26,26 @@ struct TimelineView: View {
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .leading) {
|
||||
RoundedRectangle(cornerRadius: cornerRadius)
|
||||
.foregroundColor(.blue)
|
||||
.frame(maxHeight: height)
|
||||
Group {
|
||||
RoundedRectangle(cornerRadius: cornerRadius)
|
||||
.foregroundColor(.blue)
|
||||
.frame(maxHeight: height)
|
||||
|
||||
RoundedRectangle(cornerRadius: cornerRadius)
|
||||
.fill(
|
||||
Color.green
|
||||
)
|
||||
.frame(maxHeight: height)
|
||||
.frame(width: current * oneUnitWidth)
|
||||
RoundedRectangle(cornerRadius: cornerRadius)
|
||||
.fill(Color.green)
|
||||
.frame(maxHeight: height)
|
||||
.frame(width: current * oneUnitWidth)
|
||||
|
||||
segmentsLayers
|
||||
segmentsLayers
|
||||
}
|
||||
.mask(RoundedRectangle(cornerRadius: 3))
|
||||
|
||||
Circle()
|
||||
.strokeBorder(.gray, lineWidth: 1)
|
||||
.background(Circle().fill(dragging ? .gray : .white))
|
||||
.offset(x: thumbOffset)
|
||||
.foregroundColor(.red.opacity(0.6))
|
||||
|
||||
.frame(maxHeight: height * 2)
|
||||
.frame(maxHeight: height * 4)
|
||||
|
||||
#if !os(tvOS)
|
||||
.gesture(
|
||||
@ -114,7 +114,7 @@ struct TimelineView: View {
|
||||
var projectedValue: Double {
|
||||
let change = (dragOffset / size.width) * units
|
||||
let projected = draggedFrom + change
|
||||
return projected.isFinite ? projected : start
|
||||
return projected.isFinite ? (duration - projected < (0.03 * duration) ? duration : projected) : start
|
||||
}
|
||||
|
||||
var thumbOffset: Double {
|
||||
@ -192,6 +192,7 @@ struct TimelineView_Previews: PreviewProvider {
|
||||
TimelineView(duration: .constant(100), current: .constant(90))
|
||||
TimelineView(duration: .constant(100), current: .constant(100))
|
||||
}
|
||||
.environmentObject(PlayerModel())
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
@ -138,17 +138,7 @@ struct VideoPlayerView: View {
|
||||
hoveringPlayer = hovering
|
||||
hovering ? playerControls.show() : playerControls.hide()
|
||||
}
|
||||
#if os(iOS)
|
||||
.onSwipeGesture(
|
||||
up: {
|
||||
withAnimation {
|
||||
fullScreenDetails = true
|
||||
}
|
||||
},
|
||||
down: { player.hide() }
|
||||
)
|
||||
|
||||
#elseif os(macOS)
|
||||
#if os(macOS)
|
||||
.onAppear(perform: {
|
||||
NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
|
||||
if hoveringPlayer {
|
||||
@ -269,20 +259,31 @@ struct VideoPlayerView: View {
|
||||
}
|
||||
|
||||
func playerPlaceholder(geometry: GeometryProxy) -> some View {
|
||||
HStack {
|
||||
Spacer()
|
||||
VStack {
|
||||
ZStack(alignment: .topLeading) {
|
||||
HStack {
|
||||
Spacer()
|
||||
VStack(spacing: 10) {
|
||||
#if !os(tvOS)
|
||||
Image(systemName: "ticket")
|
||||
.font(.system(size: 120))
|
||||
#endif
|
||||
VStack {
|
||||
Spacer()
|
||||
VStack(spacing: 10) {
|
||||
#if !os(tvOS)
|
||||
Image(systemName: "ticket")
|
||||
.font(.system(size: 120))
|
||||
#endif
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.foregroundColor(.gray)
|
||||
Spacer()
|
||||
}
|
||||
|
||||
Button {
|
||||
player.hide()
|
||||
} label: {
|
||||
Image(systemName: "xmark")
|
||||
.font(.system(size: 40))
|
||||
}
|
||||
.padding(10)
|
||||
.foregroundColor(.gray)
|
||||
Spacer()
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: geometry.size.width / Self.defaultAspectRatio)
|
||||
|
Loading…
Reference in New Issue
Block a user