Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/gui/macosx/library/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 31 kB image not shown  

Quelle  VLCLibraryWindow.m  Sprache: unbekannt

 
Spracherkennung für: .m vermutete Sprache: Masm {Masm[65] Apollo[55] Postscript[55]} [Methode: maximale Elemente, drei Dimensionen]

/*****************************************************************************
 * VLCLibraryWindow.m: MacOS X interface module
 *****************************************************************************
 * Copyright (C) 2019 VLC authors and VideoLAN
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan -dot- org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#import "VLCLibraryWindow.h"

#import <QuartzCore/CAMediaTimingFunction.h>

#import <vlc_common.h>
#import <vlc_configuration.h>
#import <vlc_media_library.h>
#import <vlc_url.h>

#import "VLCLibraryDataTypes.h"

#import "extensions/NSAnimationContext+VLCAdditions.h"
#import "extensions/NSColor+VLCAdditions.h"
#import "extensions/NSFont+VLCAdditions.h"
#import "extensions/NSImage+VLCAdditions.h"
#import "extensions/NSString+Helpers.h"
#import "extensions/NSView+VLCAdditions.h"
#import "extensions/NSWindow+VLCAdditions.h"

#import "library/VLCInputItem.h"
#import "library/VLCLibraryAbstractMediaLibrarySegmentViewController.h"
#import "library/VLCLibraryCollectionViewSupplementaryElementView.h"
#import "library/VLCLibraryController.h"
#import "library/VLCLibraryDynamicToolbarFlagsCapable.h"
#import "library/VLCLibraryDynamicViewModeCapable.h"
#import "library/VLCLibraryModel.h"
#import "library/VLCLibrarySegment.h"
#import "library/VLCLibrarySortingMenuController.h"
#import "library/VLCLibraryWindowChaptersSidebarViewController.h"
#import "library/VLCLibraryWindowNavigationSidebarViewController.h"
#import "library/VLCLibraryWindowPersistentPreferences.h"
#import "library/VLCLibraryWindowSidebarRootViewController.h"
#import "library/VLCLibraryWindowSplitViewController.h"
#import "library/VLCLibraryWindowToolbarDelegate.h"

#import "library/audio-library/VLCLibraryAlbumTableCellView.h"
#import "library/audio-library/VLCLibraryAudioDataSource.h"
#import "library/audio-library/VLCLibraryAudioViewController.h"

#import "library/groups-library/VLCLibraryGroupsViewController.h"

#import "library/home-library/VLCLibraryHomeViewController.h"

#import "library/playlist-library/VLCLibraryPlaylistViewController.h"

#import "library/video-library/VLCLibraryVideoDataSource.h"
#import "library/video-library/VLCLibraryVideoViewController.h"

#import "main/VLCApplication.h"
#import "main/VLCMain.h"

#import "media-source/VLCLibraryMediaSourceViewController.h"
#import "media-source/VLCLibraryMediaSourceViewNavigationStack.h"
#import "media-source/VLCMediaSourceBaseDataSource.h"

#import "menus/VLCMainMenu.h"

#import "playqueue/VLCPlayerController.h"
#import "playqueue/VLCPlayQueueController.h"
#import "playqueue/VLCPlayQueueModel.h"

#import "views/VLCBottomBarView.h"
#import "views/VLCDragDropView.h"
#import "views/VLCImageView.h"
#import "views/VLCLoadingOverlayView.h"
#import "views/VLCNoResultsLabel.h"
#import "views/VLCPlaybackEndViewController.h"
#import "views/VLCRoundedCornerTextField.h"
#import "views/VLCTrackingView.h"
#import "views/VLCUIUnits.h"

#import "windows/VLCDetachedAudioWindow.h"
#import "windows/VLCOpenInputMetadata.h"
#import "windows/VLCOpenWindowController.h"

#ifdef ENABLE_MACOSX_UI_SHADERS
# import "views/VLCSnowEffectView.h"
#endif

#import "windows/controlsbar/VLCMainWindowControlsBar.h"

#import "windows/video/VLCMainVideoViewController.h"
#import "windows/video/VLCVideoOutputProvider.h"
#import "windows/video/VLCVoutView.h"

const NSUInteger VLCLibrarySearchMaxMatchingTitles = 5;
const CGFloat VLCLibraryWindowMinimalWidth = 604.;
const CGFloat VLCLibraryWindowMinimalHeight = 307.;
const NSUserInterfaceItemIdentifier VLCLibraryWindowIdentifier = @"VLCLibraryWindow";
NSString * const VLCLibraryWindowEmbeddedVideoPlaybackActiveKey = @"embeddedVideoPlaybackActive";

@interface VLCLibraryWindow () <NSControlTextEditingDelegate>
{
    NSInteger _currentSelectedViewModeSegment;
    VLCVideoWindowCommon *_temporaryAudioDecorativeWindow;
    NSView *_acquiredVideoView;
}

@property NSTimer *searchInputTimer;

@end

static int ShowFullscreenController(vlc_object_t * __unused p_this,
                                    const char * __unused psz_variable,
                                    vlc_value_t __unused old_val,
                                    vlc_value_t __unused new_val,
                                    void * __unused param)
{
    @autoreleasepool {
        dispatch_async(dispatch_get_main_queue(), ^{
            [NSNotificationCenter.defaultCenter postNotificationName:VLCVideoWindowShouldShowFullscreenController
                                                                object:nil];
        });

        return VLC_SUCCESS;
    }
}

static int ShowController(vlc_object_t * __unused p_this,
                          const char * __unused psz_variable,
                          vlc_value_t __unused old_val,
                          vlc_value_t __unused new_val,
                          void * __unused param)
{
    @autoreleasepool {
        dispatch_async(dispatch_get_main_queue(), ^{
            [NSNotificationCenter.defaultCenter postNotificationName:VLCWindowShouldShowController
                                                                object:nil];
        });

        return VLC_SUCCESS;
    }
}

@interface VLCLibraryWindow ()

@property BOOL presentLoadingOverlayOnVideoPlaybackHide;

@end

@implementation VLCLibraryWindow

- (void)awakeFromNib
{
    [super awakeFromNib];
    self.identifier = VLCLibraryWindowIdentifier;
    self.minSize = NSMakeSize(VLCLibraryWindowMinimalWidth, VLCLibraryWindowMinimalHeight);

    self.tabbingMode = NSWindowTabbingModeDisallowed;

    VLCMain *mainInstance = VLCMain.sharedInstance;
    _playQueueController = [mainInstance playQueueController];

    libvlc_int_t *libvlc = vlc_object_instance(getIntf());
    var_AddCallback(libvlc, "intf-toggle-fscontrol", ShowFullscreenController, (__bridge void *)self);
    var_AddCallback(libvlc, "intf-show", ShowController, (__bridge void *)self);

    _libraryTargetView = [[NSView alloc] init];

    self.videoViewController.view.frame = self.mainSplitView.frame;
    self.videoViewController.view.hidden = YES;
    self.videoViewController.displayLibraryControls = YES;
    [self hideControlsBarImmediately];

    NSNotificationCenter *notificationCenter = NSNotificationCenter.defaultCenter;
    [notificationCenter addObserver:self
                           selector:@selector(shouldShowController:)
                               name:VLCWindowShouldShowController
                             object:nil];
    [notificationCenter addObserver:self
                           selector:@selector(playerStateChanged:)
                               name:VLCPlayerCurrentMediaItemChanged
                             object:nil];
    [notificationCenter addObserver:self
                           selector:@selector(playerCurrentMediaItemChanged:)
                               name:VLCPlayerCurrentMediaItemChanged
                             object:nil];
    [notificationCenter addObserver:self
                           selector:@selector(playerStateChanged:)
                               name:VLCPlayerStateChanged
                             object:nil];
    [notificationCenter addObserver:self
                           selector:@selector(playerTrackSelectionChanged:)
                               name:VLCPlayerTrackSelectionChanged
                             object:nil];

    [self setViewForSelectedSegment];
    [self setupLoadingOverlayView];

    self.librarySearchField.delegate = self;
}

- (void)dealloc
{
    [NSNotificationCenter.defaultCenter removeObserver:self];
    libvlc_int_t *libvlc = vlc_object_instance(getIntf());
    var_DelCallback(libvlc, "intf-toggle-fscontrol", ShowFullscreenController, (__bridge void *)self);
    var_DelCallback(libvlc, "intf-show", ShowController, (__bridge void *)self);
}

- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
    [super encodeRestorableStateWithCoder:coder];
    [coder encodeInteger:_librarySegmentType forKey:@"macosx-library-selected-segment"];
}

- (void)setupLoadingOverlayView
{
    _loadingOverlayView = [[VLCLoadingOverlayView alloc] init];
    self.loadingOverlayView.translatesAutoresizingMaskIntoConstraints = NO;
}

#pragma mark - misc. user interactions

- (void)updateGridVsListViewModeSegmentedControl
{
    _currentSelectedViewModeSegment = [self currentViewMode];
    _gridVsListSegmentedControl.selectedSegment = _currentSelectedViewModeSegment;
}

- (NSInteger)currentViewMode
{
    if ([self.librarySegmentViewController conformsToProtocol:@protocol(VLCLibraryDynamicViewModeCapable)]) {
        id<VLCLibraryDynamicViewModeCapable> const capableViewController =
            (id<VLCLibraryDynamicViewModeCapable>)self.librarySegmentViewController;
        return capableViewController.viewMode;
    }
    return [VLCLibrarySegment segmentWithSegmentType:self.librarySegmentType].viewMode;
}

- (void)setCurrentViewMode:(NSInteger)viewMode
{
    if ([self.librarySegmentViewController conformsToProtocol:@protocol(VLCLibraryDynamicViewModeCapable)]) {
        id<VLCLibraryDynamicViewModeCapable> const capableViewController =
            (id<VLCLibraryDynamicViewModeCapable>)self.librarySegmentViewController;
        capableViewController.viewMode = (VLCLibraryViewModeSegment)viewMode;
    } else {
        [VLCLibrarySegment segmentWithSegmentType:self.librarySegmentType].viewMode = viewMode;
    }
}

- (void)updateToolbarDisplayFlags
{
    NSParameterAssert([self.librarySegmentViewController conformsToProtocol:@protocol(VLCLibraryDynamicToolbarFlagsCapable)]);
    id<VLCLibraryDynamicToolbarFlagsCapable> const capableViewController =
        (id<VLCLibraryDynamicToolbarFlagsCapable>)self.librarySegmentViewController;
    [self.toolbarDelegate applyVisiblityFlags:capableViewController.toolbarDisplayFlags];
}

- (void)applyToolbarDisplayFlagsForSegment:(VLCLibrarySegment *)segment
{
    VLCLibraryWindowToolbarDisplayFlags flags;
    if ([self.librarySegmentViewController conformsToProtocol:@protocol(VLCLibraryDynamicToolbarFlagsCapable)]) {
        id<VLCLibraryDynamicToolbarFlagsCapable> const capableViewController =
            (id<VLCLibraryDynamicToolbarFlagsCapable>)self.librarySegmentViewController;
        flags = capableViewController.toolbarDisplayFlags;
    } else {
        flags = segment.toolbarDisplayFlags;
    }
    [self.toolbarDelegate applyVisiblityFlags:flags];
}

- (void)setViewForSelectedSegment
{
    const VLCLibrarySegmentType segmentType = self.librarySegmentType;
    VLCLibrarySegment * const segment = [VLCLibrarySegment segmentWithSegmentType:segmentType];
    [self applySegmentView:segment];
}

- (void)applySegmentView:(VLCLibrarySegment *)segment
{
    self.librarySearchField.placeholderString = segment.searchFieldPlaceholder;
    if (![self.librarySegmentViewController isKindOfClass:segment.libraryViewControllerClass]) {
        _librarySegmentViewController = [segment newLibraryViewController];
    }
    [self applyToolbarDisplayFlagsForSegment:segment];
    [segment presentLibraryViewUsingController:self.librarySegmentViewController];
    [self invalidateRestorableState];
}

- (void)setLibrarySegmentType:(NSInteger)segmentType
{
    if (segmentType == _librarySegmentType) {
        return;
    }

    _librarySegmentType = segmentType;
    [self setViewForSelectedSegment];
    [self updateGridVsListViewModeSegmentedControl];
}

- (IBAction)gridVsListSegmentedControlAction:(id)sender
{
    if (_gridVsListSegmentedControl.selectedSegment == _currentSelectedViewModeSegment) {
        return;
    }

    _currentSelectedViewModeSegment = _gridVsListSegmentedControl.selectedSegment;
    [self setCurrentViewMode:_currentSelectedViewModeSegment];
    [self setViewForSelectedSegment];
}

- (void)displayLibraryView:(NSView *)view
{
    view.translatesAutoresizingMaskIntoConstraints = NO;
    if ([self.libraryTargetView.subviews containsObject:self.loadingOverlayView]) {
        self.libraryTargetView.subviews = @[view, self.loadingOverlayView];
    } else {
        self.libraryTargetView.subviews = @[view];
    }

    [NSLayoutConstraint activateConstraints:@[
        [view.topAnchor constraintEqualToAnchor:self.libraryTargetView.topAnchor],
        [view.bottomAnchor constraintEqualToAnchor:self.libraryTargetView.bottomAnchor],
        [view.leftAnchor constraintEqualToAnchor:self.libraryTargetView.leftAnchor],
        [view.rightAnchor constraintEqualToAnchor:self.libraryTargetView.rightAnchor]
    ]];

#ifdef ENABLE_MACOSX_UI_SHADERS
    if (VLCMain.sharedInstance.metalLibrary && ((VLCApplication *)NSApplication.sharedApplication).winterHolidaysTheming) {
        VLCSnowEffectView * const snowView =
            [[VLCSnowEffectView alloc] initWithFrame:self.contentView.bounds];
        [self.libraryTargetView addSubview:snowView];
        snowView.translatesAutoresizingMaskIntoConstraints = NO;
        [snowView applyConstraintsToFillSuperview];
    }
#endif
}

- (void)displayLibraryPlaceholderViewWithImage:(NSImage *)image
                              usingConstraints:(NSArray<NSLayoutConstraint *> *)constraints
                             displayingMessage:(NSString *)message
{
    for (NSLayoutConstraint * const constraint in self.placeholderImageViewConstraints) {
        constraint.active = NO;
    }
    _placeholderImageViewConstraints = constraints;
    for (NSLayoutConstraint * const constraint in constraints) {
        constraint.active = YES;
    }

    [self displayLibraryView:self.emptyLibraryView];
    self.placeholderImageView.image = image;
    self.placeholderLabel.stringValue = message;
}

- (void)displayNoResultsMessage
{
    if (self.noResultsLabel == nil) {
        _noResultsLabel = [[VLCNoResultsLabel alloc] init];
        _noResultsLabel.translatesAutoresizingMaskIntoConstraints = NO;
    }

    if ([self.libraryTargetView.subviews containsObject:self.loadingOverlayView]) {
        self.libraryTargetView.subviews = @[self.noResultsLabel, self.loadingOverlayView];
    } else {
        self.libraryTargetView.subviews = @[_noResultsLabel];
    }

    [NSLayoutConstraint activateConstraints:@[
        [self.noResultsLabel.centerXAnchor constraintEqualToAnchor:self.libraryTargetView.centerXAnchor],
        [self.noResultsLabel.centerYAnchor constraintEqualToAnchor:self.libraryTargetView.centerYAnchor]
    ]];
}

- (void)presentLibraryItem:(id<VLCMediaLibraryItemProtocol>)libraryItem
{
    VLCLibrarySegment * const segment = [VLCLibrarySegment segmentForLibraryItem:libraryItem];
    [self.splitViewController.navSidebarViewController selectSegment:segment.segmentType];
    if ([self.librarySegmentViewController conformsToProtocol:@protocol(VLCLibraryItemPresentingCapable)]) {
        [(VLCLibraryAbstractSegmentViewController<VLCLibraryItemPresentingCapable> *)self.librarySegmentViewController presentLibraryItem:libraryItem];
    }
}

- (void)browseFolderByMrl:(NSString *)mrl
{
    [self goToBrowseSection:self];
    [(VLCLibraryMediaSourceViewController *)self.librarySegmentViewController browseFolderByMrl:mrl];
}

- (IBAction)sortLibrary:(id)sender
{
    if (!_librarySortingMenuController) {
        _librarySortingMenuController = [[VLCLibrarySortingMenuController alloc] init];
    }
    [NSMenu popUpContextMenu:_librarySortingMenuController.librarySortingMenu withEvent:[NSApp currentEvent] forView:sender];
}

- (void)stopSearchTimer
{
    [self.searchInputTimer invalidate];
    self.searchInputTimer = nil;
}

- (IBAction)filterLibrary:(id)sender
{
    [self stopSearchTimer];
    self.searchInputTimer = [NSTimer scheduledTimerWithTimeInterval:0.3
                                                            target:self
                                                           selector:@selector(updateFilterString)
                                                           userInfo:nil
                                                            repeats:NO];

    if (self.librarySegmentType == VLCLibraryHomeSegmentType) {
        return;
    }

    // Trigger completions
    NSText * const fieldEditor = self.librarySearchField.currentEditor;
    if (fieldEditor) {
        [fieldEditor complete:sender];
    }
}

- (void)updateFilterString
{
    if (self.librarySegmentType == VLCLibraryHomeSegmentType) {
        return;
    }

    [VLCMain.sharedInstance.libraryController filterByString:_librarySearchField.stringValue];
}

- (void)clearFilterString
{
    [self stopSearchTimer];
    _librarySearchField.stringValue = @"";
    [self updateFilterString];
}

- (BOOL)handlePasteBoardFromDragSession:(NSPasteboard *)paste
{
    return [VLCFileDragRecognisingView handlePasteboardFromDragSessionAsPlayQueueItems:paste];
}

- (IBAction)goToBrowseSection:(id)sender
{
    [self.splitViewController.navSidebarViewController selectSegment:VLCLibraryBrowseSegmentType];
}

- (IBAction)backwardsNavigationAction:(id)sender
{
    !self.embeddedVideoPlaybackActive
        ? [((VLCLibraryMediaSourceViewController *)self.librarySegmentViewController).navigationStack backwards]
        : [self disableVideoPlaybackAppearance];
}

- (IBAction)forwardsNavigationAction:(id)sender
{
    [((VLCLibraryMediaSourceViewController *)self.librarySegmentViewController).navigationStack forwards];
}

#pragma mark - video output controlling

- (void)playerStateChanged:(NSNotification *)notification
{
    if (_playQueueController.playerController.playerState == VLC_PLAYER_STATE_STOPPED) {
        [self hideControlsBar];
        return;
    }

    if (!self.embeddedVideoPlaybackActive) {
        [self showControlsBar];
    }
}

- (void)playerCurrentMediaItemChanged:(NSNotification *)notification
{
    NSParameterAssert(notification);
    VLCPlayerController * const controller = notification.object;
    NSAssert(controller != nil,
             @"Player current media item changed notification should have valid player controller");

    // Live video playback in controls bar artwork button handling
    if (self.splitViewController.mainVideoModeEnabled) {
        return;
    }

    if (controller.currentMediaIsAudioOnly && _acquiredVideoView) {
        [self.videoViewController returnVideoView:_acquiredVideoView];
        _acquiredVideoView = nil;
    } else if (!controller.currentMediaIsAudioOnly && _acquiredVideoView == nil) {
        [self configureArtworkButtonLiveVideoView];
    }
}

- (void)playerTrackSelectionChanged:(NSNotification *)notification
{
    [self updateArtworkButtonEnabledState];
}

- (void)updateArtworkButtonEnabledState
{
    VLCPlayerController * const playerController = self.playerController;
    const BOOL videoTrackDisabled =
        !playerController.videoTracksEnabled || !playerController.selectedVideoTrack.selected;
    const BOOL audioTrackDisabled =
        !playerController.audioTracksEnabled || !playerController.selectedAudioTrack.selected;
    const BOOL currentItemIsAudio =
        playerController.videoTracks.count == 0 && playerController.audioTracks.count > 0;
    const BOOL pipOpen = self.videoViewController.pipIsActive;
    const BOOL artworkButtonDisabled =
        (videoTrackDisabled && audioTrackDisabled) ||
        (videoTrackDisabled && !currentItemIsAudio) ||
        pipOpen;
    self.artworkButton.enabled = !artworkButtonDisabled;
    self.artworkButton.hidden = artworkButtonDisabled;
    self.controlsBar.thumbnailTrackingView.enabled = !artworkButtonDisabled;
    for (NSView * const view in self.controlsBar.thumbnailTrackingView.viewsToHide) {
        view.hidden = artworkButtonDisabled;
    }
}

- (void)configureArtworkButtonLiveVideoView
{
    VLCPlayerController * const playerController = self.playerController;
    const BOOL videoTrackDisabled =
        !playerController.videoTracksEnabled || !playerController.selectedVideoTrack.selected;

    if (videoTrackDisabled || !var_InheritBool(getIntf(), "embedded-video"))
        return;

    _acquiredVideoView = [self.videoViewController acquireVideoView];
    if (_acquiredVideoView) {
        [self.controlsBar.artworkImageView addSubview:_acquiredVideoView
                                           positioned:NSWindowBelow
                                           relativeTo:self.artworkButton];
        [_acquiredVideoView applyConstraintsToFillSuperview];
    }
}

- (void)hideControlsBarImmediately
{
    self.controlsBar.bottomBarView.hidden = YES;
    self.controlsBar.bottomBarView.alphaValue = 0;
}

- (void)hideControlsBar
{
     [NSAnimationContext runAnimationRespectingPreferencesWithDuration:VLCUIUnits.controlsFadeAnimationDuration
                                                               changes:^(NSAnimationContext * const context) {
        context.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
        self.controlsBar.bottomBarView.animator.alphaValue = 0;
    } completionHandler:^{
        self.controlsBar.bottomBarView.hidden = self.controlsBar.bottomBarView.alphaValue == 0;
    }];
}

- (void)showControlsBarImmediately
{
    self.controlsBar.bottomBarView.hidden = NO;
    self.controlsBar.bottomBarView.alphaValue = 1;
}

- (void)showControlsBar
{
    self.controlsBar.bottomBarView.hidden = NO;

    [NSAnimationContext runAnimationRespectingPreferencesWithDuration:VLCUIUnits.controlsFadeAnimationDuration
                                                              changes:^(NSAnimationContext * const __unused context) {
        context.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
        self.controlsBar.bottomBarView.animator.alphaValue = 1;
    } completionHandler:nil];
}

- (BOOL)shouldShowControlsBarForPlaybackContext
{
    VLCPlayerController * const playerController = self.playQueueController.playerController;
    return playerController.playerState != VLC_PLAYER_STATE_STOPPED &&
           self.playQueueController.playQueueModel.numberOfPlayQueueItems > 0;
}

- (void)presentExternalWindows
{
    VLCVideoOutputProvider * const voutProvider = VLCMain.sharedInstance.voutProvider;
    NSArray<NSWindow *> * const voutWindows = voutProvider.voutWindows.allValues;

    if (voutWindows.count == 0 && self.playerController.videoTracks.count == 0) {
        // If we have no video windows in the video provider but are being asked to present a window
        // then we are dealing with an audio item and the user wants to see the decorative artwork
        // window for said audio
        [VLCMain.sharedInstance.detachedAudioWindow makeKeyAndOrderFront:self];
        return;
    }

    for (NSWindow * const window in voutWindows) {
        [window makeKeyAndOrderFront:self];
    }
}

- (void)presentVideoView
{
    for (NSView *subview in _libraryTargetView.subviews) {
        [subview removeFromSuperview];
    }

    NSLog(@"Presenting video view in main library window.");

    NSView * const videoView = self.videoViewController.view;
    videoView.translatesAutoresizingMaskIntoConstraints = NO;
    videoView.hidden = NO;

    [_libraryTargetView addSubview:videoView];
    [videoView applyConstraintsToFillSuperview];
}

- (void)enableVideoPlaybackAppearance
{
    VLCPlayerController * const playerController = self.playerController;
    const BOOL videoTrackDisabled =
        !playerController.videoTracksEnabled || !playerController.selectedVideoTrack.selected;
    const BOOL audioTrackDisabled =
        !playerController.audioTracksEnabled || !playerController.selectedAudioTrack.selected;
    const BOOL currentItemIsAudio =
        playerController.videoTracks.count == 0 && playerController.audioTracks.count > 0;
    if ((videoTrackDisabled && audioTrackDisabled) || (videoTrackDisabled && !currentItemIsAudio)) {
        return;
    }

    const BOOL isEmbedded = var_InheritBool(getIntf(), "embedded-video");
    if (!isEmbedded) {
        [self presentExternalWindows];
        return;
    }

    NSAppearance *darkAppearance = nil;
    if (@available(macOS 10.14, *)) {
        darkAppearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
    } else {
        darkAppearance = [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark];
    }

    if (darkAppearance) {
        self.appearance = darkAppearance;
    }

    if (_acquiredVideoView) {
        [self.videoViewController returnVideoView:_acquiredVideoView];
        _acquiredVideoView = nil;
    }

    [self presentVideoView];
    [self enableVideoTitleBarMode];
    [self hideControlsBarImmediately];
    [self.videoViewController showControls];

    self.splitViewController.mainVideoModeEnabled = YES;

    [self willChangeValueForKey:VLCLibraryWindowEmbeddedVideoPlaybackActiveKey];
    _embeddedVideoPlaybackActive = YES;
    [self didChangeValueForKey:VLCLibraryWindowEmbeddedVideoPlaybackActiveKey];

    if ([self.librarySegmentViewController isKindOfClass:VLCLibraryAbstractMediaLibrarySegmentViewController.class]) {
        [(VLCLibraryAbstractMediaLibrarySegmentViewController *)self.librarySegmentViewController disconnect];
    }
}

- (void)disableVideoPlaybackAppearance
{
    self.appearance = nil;

    [self makeFirstResponder:self.splitViewController.multifunctionSidebarViewController.view];
    [VLCMain.sharedInstance.voutProvider updateWindowLevelForHelperWindows:NSNormalWindowLevel];

    // restore alpha value to 1 for the case that macosx-opaqueness is set to < 1
    self.alphaValue = 1.0;
    [self disableVideoTitleBarMode];
    [self setViewForSelectedSegment];
    if ([self shouldShowControlsBarForPlaybackContext]) {
        [self showControlsBarImmediately];
    } else {
        [self hideControlsBarImmediately];
    }
    [self updateArtworkButtonEnabledState];

    if (!self.playQueueController.playerController.currentMediaIsAudioOnly) {
        [self configureArtworkButtonLiveVideoView];
    }

    self.splitViewController.mainVideoModeEnabled = NO;

    [self willChangeValueForKey:VLCLibraryWindowEmbeddedVideoPlaybackActiveKey];
    _embeddedVideoPlaybackActive = NO;
    [self didChangeValueForKey:VLCLibraryWindowEmbeddedVideoPlaybackActiveKey];

    if (self.presentLoadingOverlayOnVideoPlaybackHide) {
        [self showLoadingOverlay];
    }

    if ([self.librarySegmentViewController isKindOfClass:VLCLibraryAbstractMediaLibrarySegmentViewController.class]) {
        [(VLCLibraryAbstractMediaLibrarySegmentViewController *)self.librarySegmentViewController connect];
    }
}

- (void)showLoadingOverlay
{
    if ([self.libraryTargetView.subviews containsObject:self.loadingOverlayView]) {
        return;
    } else if ([self.libraryTargetView.subviews containsObject:self.videoViewController.view]) {
        self.presentLoadingOverlayOnVideoPlaybackHide = YES;
        return;
    }

    self.loadingOverlayView.wantsLayer = YES;
    self.loadingOverlayView.alphaValue = 0.0;

    NSArray * const views = [self.libraryTargetView.subviews arrayByAddingObject:self.loadingOverlayView];
    self.libraryTargetView.subviews = views;
    [self.loadingOverlayView applyConstraintsToFillSuperview];

    [NSAnimationContext runAnimationRespectingPreferencesWithDuration:0.5
                                                              changes:^(NSAnimationContext * const __unused context) {
        self.loadingOverlayView.animator.alphaValue = 1.0;
    } completionHandler:nil];

    [self.loadingOverlayView.indicator startAnimation:self];

}

- (void)hideLoadingOverlay
{
    self.presentLoadingOverlayOnVideoPlaybackHide = NO;

    if (![self.libraryTargetView.subviews containsObject:self.loadingOverlayView]) {
        return;
    }

    self.loadingOverlayView.wantsLayer = YES;
    self.loadingOverlayView.alphaValue = 1.0;

    [NSAnimationContext runAnimationRespectingPreferencesWithDuration:1.0
                                                              changes:^(NSAnimationContext * const __unused context) {
        self.loadingOverlayView.animator.alphaValue = 0.0;
    } completionHandler:^{
        NSMutableArray * const mutableSubviews = self.libraryTargetView.subviews.mutableCopy;
        [mutableSubviews removeObject:self.loadingOverlayView];
        self.libraryTargetView.subviews = mutableSubviews;
        [self.loadingOverlayView.indicator stopAnimation:self];
    }];
}

- (void)mouseMoved:(NSEvent *)o_event
{
    if (self.embeddedVideoPlaybackActive) {
        NSPoint mouseLocation = [o_event locationInWindow];
        NSView *videoView = self.videoViewController.view;
        NSRect videoViewRect = [videoView convertRect:videoView.frame toView:self.contentView];

        if ([self.contentView mouse:mouseLocation inRect:videoViewRect]) {
            [NSNotificationCenter.defaultCenter postNotificationName:VLCVideoWindowShouldShowFullscreenController
                                                                object:self];
        }
    }

    [super mouseMoved:o_event];
}

#pragma mark - NSControlTextEditingDelegate

- (NSArray<NSString *> *)control:(NSControl *)control
                         textView:(NSTextView *)textView
                      completions:(NSArray<NSString *> *)words
              forPartialWordRange:(NSRange)charRange
              indexOfSelectedItem:(NSInteger *)index
{
    // Get the partial word being completed
    NSString * const currentText = textView.string;
    NSString * const partialWord = [currentText substringWithRange:charRange];

    if (partialWord.length == 0) {
        return @[];
    }

    // Get cached titles from library model
    VLCLibraryModel * const libraryModel = VLCMain.sharedInstance.libraryController.libraryModel;
    if (!libraryModel) {
        return @[];
    }

    NSArray<NSString *> * const allTitles = libraryModel.listOfMediaTitles;
    NSMutableArray<NSString *> * const matchingTitles = [NSMutableArray arrayWithCapacity:VLCLibrarySearchMaxMatchingTitles];

    // Filter titles that match the partial word
    for (NSString * const title in allTitles) {
        if (![title.lowercaseString hasPrefix:partialWord.lowercaseString]) {
            continue;
        }
        [matchingTitles addObject:title];
        if (matchingTitles.count >= VLCLibrarySearchMaxMatchingTitles) {
            break;
        }
    }

    *index = -1;
    return matchingTitles;
}

#pragma mark -
#pragma mark respond to core events

- (void)shouldShowController:(NSNotification *)aNotification
{
    [self makeKeyAndOrderFront:nil];

    if (self.videoViewController.view.isHidden) {
        [self showControlsBar];
        NSView *standardWindowButtonsSuperView = [self standardWindowButton:NSWindowCloseButton].superview;
        standardWindowButtonsSuperView.hidden = NO;
    }
}

- (void)windowWillEnterFullScreen:(NSNotification *)notification
{
    [super windowWillEnterFullScreen:notification];

    if (self.splitViewController.mainVideoModeEnabled) {
        [self hideControlsBar];
    }

    if (self.splitViewController.mainVideoModeEnabled) {
        self.splitViewController.multifunctionSidebarItem.animator.collapsed = YES;
    }
}

- (void)windowDidEnterFullScreen:(NSNotification *)notification
{
    [super windowDidEnterFullScreen:notification];
    if (!self.splitViewController.mainVideoModeEnabled) {
        [self showControlsBar];
    }
}

@end

[Dauer der Verarbeitung: 0.4 Sekunden, vorverarbeitet 2026-10-24]