vpr/ShadowStream/Views/MainWindow.xaml.cs

1986 lines
70 KiB
C#

using file_finder__test;
using LibVLCSharp.Shared;
using ModuleManager.Modules;
using NotVPR_SideProjecktForVpr_FreeTime;
using ShadowStream;
using ShadowStream.LogHelper;
using ShadowStream.Modules;
using ShadowStream.Obejeckte;
using ShadowStream.ObjecktForJason;
using ShadowStream.Views;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Net.Mime;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Brushes = System.Windows.Media.Brushes;
using Color = System.Windows.Media.Color;
using File = System.IO.File;
using Path = System.IO.Path;
using Point = System.Windows.Point;
namespace ModuleManager;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
//Quinn and Reda and Yazan
public partial class MainWindow : Window
{ //quinn
#region no dont cahnge
//adding base components used threw out the code
LogWindow logWin = new LogWindow(log.GetEntries());
static LogHelper log = new LogHelper();
PlaylistEditor popupWindow;
ObjListBP _playlists = new ObjListBP();
private Catagory Muvie = new Catagory("Muvie");
private Catagory Serie = new Catagory("Serie");
private Catagory Music = new Catagory("Music");
private Catagory Photo = new Catagory("Photos");
private Catagory Favorites;
List<string> suportedVidioFiles = new List<string>();
List<string> suportedMusicFiles = new List<string>();
List<string> suportedPhotoFiles = new List<string>();
//root directory
List<string> dirs = new List<string>();
private int option = 2;
int specificOption = 0;
string rootPath = "M:/Media";
FileScanner fileScanner;
//video player variables
public string _path ;
public string _category;
StringConversions stringConversions = new StringConversions();
private DispatcherTimer _progressTimer;
#endregion
//Quinn
#region vlc logic
private LibVLC _libVLC;
private LibVLCSharp.Shared.MediaPlayer _mediaPlayer;
#endregion
//code start
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
FirstCat?.Focus();
}
public MainWindow()
{
#region Init wpf
InitializeComponent();
//Initialise but Hide
//logWin.Show();
//logWin.Hide();
//this.Hide();
//Begin Login Process
var login = new LogIn();
login.Show();
//login.Owner = this;
Favorites = new Catagory("Favorites",Favorites_Home);
#endregion
//Quinn Dont change or remuve!!!
#region vlc init
Core.Initialize(); // Important: load native libvlc binaries
_libVLC = new LibVLC();
_mediaPlayer = new LibVLCSharp.Shared.MediaPlayer(_libVLC);
VideoView.MediaPlayer = _mediaPlayer;
_mediaPlayer.Volume = Convert.ToInt32(vol.Value);
_progressTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_progressTimer.Tick += ProgressTimer_Tick;
#endregion
#region BlutuethLogic
InitBluetoothServer();
#endregion
#region only exdend. no remuving of code
//adding all extensions... example values added
suportedVidioFiles.Add(".mp4");
suportedVidioFiles.Add(".mkv");
suportedMusicFiles.Add(".wav");
suportedMusicFiles.Add(".mp3");
suportedPhotoFiles.Add(".jpg");
Createscan();
//dirs.Add("C:/");
//execute and wait for task completion
var tmp = fileScanner.ScanAllDrivesAsync();
//load json parallel to finding drives
//add code laiter
tmp.Wait();
foreach (var VARIABLE in tmp.Result)
{
dirs.Add(VARIABLE);
}
#endregion
LoadSavedData();
}
//Quinn
#region dont change
//reminder to self. if issue acures. check if anyone changed folder names/or if paths are being refrenced wrongly
#region ScannLogic
void Createscan()
{
//adding all string arrays to one temp array to do the initial scan of the designated drive
int count = 0;
string[] tmp = new string[suportedMusicFiles.Count + suportedVidioFiles.Count + suportedPhotoFiles.Count];
foreach (var suportedVidioFile in suportedVidioFiles)
{
tmp[count] = suportedVidioFile;
count++;
}
foreach (var suportedMusicFile in suportedMusicFiles)
{
tmp[count] = suportedMusicFile;
count++;
}
foreach (var suportedPhotoFile in suportedPhotoFiles)
{
tmp[count] = suportedPhotoFile;
count++;
}
//initialise the file scanner with all files endings wanted
fileScanner = new FileScanner(tmp);
}
#endregion
#region Click Methods
//-_- warum klapt es nicht
private ProgressBar progressScann;
private void OnItemPlayButtonClick(object sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is string filePath)
{
string[] parts = filePath.Split("||");
string left = parts[0];
PlayVideo(left);
}
}
private void OnItemNextButtonClick(object sender, RoutedEventArgs e)
{
setStrings();
switch (_category.ToLower())
{
case "muvie":
videoArrows(ref Muvie,true); break;
case "serie":
videoArrows(ref Serie,true);break;
case "music":
videoArrows(ref Music,true); break;
case "photos":
videoArrows(ref Photo,true); break;
default:
break;
}
}
private void OnItemPriorButtonClick(object sender, RoutedEventArgs e)
{
setStrings();
switch (_category.ToLower())
{
case "muvie":
videoArrows(ref Muvie,false); break;
case "serie":
videoArrows(ref Serie,false);break;
case "music":
videoArrows(ref Music,false); break;
case "photos":
videoArrows(ref Photo,false); break;
default:
break;
}
}
private void OnItemPauseBtn_Click(object sender, RoutedEventArgs e)
{
if (_mediaPlayer.IsPlaying)
{
_mediaPlayer.Pause(); // Pauses the video
}
else
{
_mediaPlayer.SetPause(false); // Unpauses the video
}
}
private void Close_Player(object sender, RoutedEventArgs e)
{
_mediaPlayer.Stop();
VideoView.Visibility = Visibility.Collapsed;
}
private async void scanButton_Click(object sender, RoutedEventArgs e)
{
try
{
MessageBoxResult result =
MessageBox.Show(
"Do you want to replace existing items\nyes will delite existing items/no keeps them",
"replace items", MessageBoxButton.YesNoCancel);
if (result == MessageBoxResult.Cancel)
return;
else if (result == MessageBoxResult.Yes)
{
Muvie.clear();
Serie.clear();
Music.clear();
Photo.clear();
}
log.Log("Scanning files...");
List<string> tmp = new List<string>();
switch (option)
{
case 0:
foreach (var VARIABLE in dirs)
{
var scanResult = await fileScanner.ScanDriveParallel(VARIABLE);
tmp.AddRange(scanResult);
}
break;
case 1:
var scanResult1 = await fileScanner.ScanDriveParallel(dirs[specificOption]);
tmp.AddRange(scanResult1);
break;
case 2:
var scanResult2 = await fileScanner.ScanDriveParallel(rootPath);
tmp.AddRange(scanResult2);
break;
}
log.Log($"Total scanned files: {tmp.Count}");
var classifier = new FileClassifier();
var (musicFiles, videoFiles, photoFiles) = await classifier.ClassifyFilesAsync(tmp, suportedMusicFiles, suportedVidioFiles, suportedPhotoFiles);
var separator = new VideoSeparator();
var (series, movies) = await separator.SeparateVideosAsync(videoFiles);
log.Log($"musicFiles count: {musicFiles.Count}");
log.Log($"videoFiles count: {videoFiles.Count}");
log.Log($"photoFiles count: {photoFiles.Count}");
log.Log($"series count: {series.Count}");
log.Log($"movies count: {movies.Count}");
Console.WriteLine($"Total scanned files: {tmp.Count}");
Console.WriteLine($"Total video files: {videoFiles.Count}");
Console.WriteLine($"Total musicFiles count: {musicFiles.Count}");
Console.WriteLine($"Total photo files: {photoFiles.Count}");
Console.WriteLine($"Total series count: {series.Count}");
Console.WriteLine($"Total muvies count: {movies.Count}");
if (result == MessageBoxResult.No)
{
var tmp2 = new List<string>();
foreach (var VARIABLE in musicFiles)
{
if (!Music.getLink().Contains(VARIABLE)) ;
tmp2.Add(VARIABLE);
}
musicFiles.Clear();
musicFiles.AddRange(tmp2);
tmp2.Clear();
foreach (var VARIABLE in series)
{
if (!Serie.getLink().Contains(VARIABLE)) ;
tmp2.Add(VARIABLE);
}
series.Clear();
series.AddRange(tmp2);
tmp2.Clear();
foreach (var VARIABLE in movies)
{
if(!Muvie.getLink().Contains(VARIABLE)) ;
tmp2.Add(VARIABLE);
}
movies.Clear();
movies.AddRange(tmp2);
tmp2.Clear();
foreach (var VARIABLE in photoFiles)
{
if(!Photo.getLink().Contains(VARIABLE))
tmp2.Add(VARIABLE);
}
photoFiles.Clear();
photoFiles.AddRange(tmp2);
tmp2.Clear();
}
progressScann = new ProgressBar("ImageGen", musicFiles.Count + videoFiles.Count + photoFiles.Count);
progressScann.Show();
videoFiles = null;
log.Log("files sorted");
List<locJason> muviesJS = new List<locJason>();
List<locJason> seriesJS = new List<locJason>();
List<locJason> photosJS = new List<locJason>();
List<locJason> musicJS = new List<locJason>();
JasonToString jasonToString = new JasonToString();
var movieItems = await ItemCreater(movies, "Muvie", false);
foreach (var item in movieItems)
{
BitmapImage bitmapImage = item.getImage();
if (bitmapImage != null)
{
try
{
string base64Image = jasonToString.BitmapToBase64String(
BitmapConversions.BitmapImageToBitmap(bitmapImage));
Muvie.addItem(item);
muviesJS.Add(new locJason
{
path = item.getLink(),
imageData = base64Image,
type = item.getType()
});
}
catch (Exception exception)
{
Console.WriteLine($"Error processing movie item {item.getLink()}: {exception.Message}");
log.Error($"Error processing movie item {item.getLink()}: {exception}");
}
}
else
{
Console.WriteLine($"Warning: Movie item {item.getLink()} has no image data.");
log.Log($"Warning: Movie item {item.getLink()} has no image data.");
}
}
var seriesItems = await ItemCreater(series, "Serie", false);
foreach (var item in seriesItems)
{
BitmapImage bitmapImage = item.getImage();
if (bitmapImage != null)
{
try
{
string base64Image = jasonToString.BitmapToBase64String(
BitmapConversions.BitmapImageToBitmap(bitmapImage));
Serie.addItem(item);
seriesJS.Add(new locJason
{
path = item.getLink(),
imageData = base64Image,
type = item.getType()
});
}
catch (Exception exception)
{
Console.WriteLine($"Error processing series item {item.getLink()}: {exception.Message}");
log.Error($"Error processing series item {item.getLink()}: {exception}");
}
}
else
{
Console.WriteLine($"Warning: Series item {item.getLink()} has no image data.");
log.Log($"Warning: Series item {item.getLink()} has no image data.");
}
}
var photoItems = await ItemCreater(photoFiles, "Photo", true);
foreach (var item in photoItems)
{
BitmapImage bitmapImage = item.getImage();
if (bitmapImage != null)
{
try
{
string base64Image = jasonToString.BitmapToBase64String(
BitmapConversions.BitmapImageToBitmap(bitmapImage));
Photo.addItem(item);
photosJS.Add(new locJason
{
path = item.getLink(),
imageData = base64Image,
type = item.getType()
});
}
catch (Exception exception)
{
Console.WriteLine($"Error processing photo item {item.getLink()}: {exception.Message}");
log.Error($"Error processing photo item {item.getLink()}: {exception}");
}
}
else
{
Console.WriteLine($"Warning: Photo item {item.getLink()} has no image data.");
log.Log($"Warning: Photo item {item.getLink()} has no image data.");
}
}
var musicItems = await ItemCreater(musicFiles, "Music", false);
foreach (var item in musicItems)
{
BitmapImage bitmapImage = item.getImage();
if (bitmapImage != null)
{
try
{
string base64Image = jasonToString.BitmapToBase64String(
BitmapConversions.BitmapImageToBitmap(bitmapImage));
Music.addItem(item);
musicJS.Add(new locJason
{
path = item.getLink(),
imageData = base64Image,
type = item.getType()
});
}
catch (Exception exception)
{
Console.WriteLine($"Error processing music item {item.getLink()}: {exception.Message}");
log.Error($"Error processing music item {item.getLink()}: {exception}");
}
}
else
{
Console.WriteLine($"Info: Music item {item.getLink()} has no image data.");
log.Log($"Info: Music item {item.getLink()} has no image data.");
}
}
log.Log("scan finished");
Jason_Writer jason_Writer = new Jason_Writer();
List<Task> tasks = new List<Task>
{
jason_Writer.SaveList("Muvies", muviesJS),
jason_Writer.SaveList("Series", seriesJS),
jason_Writer.SaveList("Photos", photosJS),
jason_Writer.SaveList("Music", musicJS)
};
await Task.WhenAll(tasks);
musicJS = null;
seriesJS = null;
photosJS = null;
MenueItems();
progressScann.Hide();
MessageBox.Show("Scan finished");
}
catch (Exception ex)
{
MessageBox.Show($"An error occurred during scanning:\n\n{ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
log.Error($"An error occurred during scanning:\n\n{ex}");
}
}
private void PlayListButton_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<Catagory> tmpBP= new ObservableCollection<Catagory>();
tmpBP.Add(Muvie);
tmpBP.Add(Serie);
tmpBP.Add(Photo);
tmpBP.Add(Music);
popupWindow = new PlaylistEditor(tmpBP,false,false);
popupWindow.Owner = this;
popupWindow.ShowInTaskbar = false;
popupWindow.Show();
}
private void EdditFav_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<Catagory> tmpBP= new ObservableCollection<Catagory>();
tmpBP.Add(Muvie);
tmpBP.Add(Serie);
tmpBP.Add(Photo);
tmpBP.Add(Music);
popupWindow = new PlaylistEditor(tmpBP,true,true,Favorites);
popupWindow.Owner = this;
popupWindow.ShowInTaskbar = false;
popupWindow.Show();
}
#region Catagory btns
private void Home_OnClick(object sender, RoutedEventArgs e)
{
Close_Player();
ScrollContentPlaylist.Visibility = Visibility.Collapsed;
ScrollContentCat.Visibility = Visibility.Collapsed;
ScrollContentHome.Visibility = Visibility.Visible;
MenueItems();
SetInitialFocusForCurrentView();
}
private void Musik_OnClick(object sender, RoutedEventArgs e)
{
Close_Player();
Name_of_Catagory_Text1.Visibility = Visibility.Collapsed;
ScrollContentPlaylist.Visibility = Visibility.Collapsed;
ScrollContentHome.Visibility = Visibility.Collapsed;
ScrollContentCat.Visibility = Visibility.Visible;
Name_of_Catagory1.Visibility = Visibility.Collapsed;
MenueItems(ref Music);
SetInitialFocusForCurrentView();
}
private void Photo_OnClick(object sender, RoutedEventArgs e)
{
Close_Player();
Name_of_Catagory_Text1.Visibility = Visibility.Collapsed;
ScrollContentPlaylist.Visibility = Visibility.Collapsed;
ScrollContentHome.Visibility = Visibility.Collapsed;
ScrollContentCat.Visibility = Visibility.Visible;
Name_of_Catagory1.Visibility = Visibility.Collapsed;
MenueItems(ref Photo);
SetInitialFocusForCurrentView();
}
private void Video_OnClick(object sender, RoutedEventArgs e)
{
Close_Player();
Name_of_Catagory_Text1.Visibility = Visibility.Visible;
ScrollContentPlaylist.Visibility = Visibility.Collapsed;
ScrollContentHome.Visibility = Visibility.Collapsed;
ScrollContentCat.Visibility = Visibility.Visible;
Name_of_Catagory1.Visibility = Visibility.Visible;
MenueItems(ref Muvie,ref Serie);
SetInitialFocusForCurrentView();
}
private void PlayListButton_OnClick(object sender, RoutedEventArgs e)
{
Close_Player();
Name_of_Catagory_Text1.Visibility = Visibility.Collapsed;
ScrollContentHome.Visibility = Visibility.Collapsed;
ScrollContentCat.Visibility = Visibility.Collapsed;
Name_of_Catagory1.Visibility = Visibility.Collapsed;
PopulateAllCategoriesFromPlaylists(); // <-- This needs to set ScrollContentPlaylist visible
SetInitialFocusForCurrentView();
}
#endregion
#endregion
#region itemCreation
public async Task<List<Item>> ItemCreater(List<string> paths, string type, bool isFoto)
{
// Determine if it's music based on 'type' string
bool isMusic = (type == "Music");
// Semaphore for concurrency control
var semaphore = new SemaphoreSlim(Math.Max(1, Environment.ProcessorCount / 2));
// ConcurrentBag for thread-safe collection of items
var itemsBag = new ConcurrentBag<Item>();
// Pre-load default music image once, if applicable
BitmapImage defaultMusicImage = null;
if (isMusic)
{
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
string imagePath = System.IO.Path.Combine(baseDir, "Resources", "Pics", "MusicD.jpeg");
if (System.IO.File.Exists(imagePath))
{
try
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
defaultMusicImage = new BitmapImage(new Uri(imagePath));
defaultMusicImage.DecodePixelWidth = 150;
defaultMusicImage.DecodePixelHeight = 100;
defaultMusicImage.Freeze(); // Crucial for cross-thread access
});
}
catch (Exception ex)
{
// Log error if default image itself cannot be loaded
Console.WriteLine($"Error loading default music image {imagePath}: {ex.Message}");
// Consider adding to your specific logger here: log.Error(...)
defaultMusicImage = null;
}
}
else
{
// Inform the user if the default image is missing
MessageBox.Show($"Default music image not found at: {imagePath}");
// Consider adding to your specific logger here: log.Error(...)
}
}
// Create a list of tasks, one for each path
var tasks = paths.Select(async path =>
{
await semaphore.WaitAsync(); // Acquire a slot from the semaphore
BitmapImage bitmapImage = null; // To hold the image for the current item
Item newItem = null; // The Item object to be created
try
{
if (isMusic) // Logic for Music files: extract album art or use default
{
bitmapImage = defaultMusicImage; // Start with the default image
TagLib.File file = null; // TagLib.File object for disposing
try
{
file = TagLib.File.Create(path); // Create TagLib file instance
if (file.Tag.Pictures.Length > 0) // Check if album art exists
{
var pic = file.Tag.Pictures[0];
var ms = new System.IO.MemoryStream(pic.Data.Data); // Get image data as MemoryStream
await Application.Current.Dispatcher.InvokeAsync(() =>
{
var img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad; // Load full image data
ms.Position = 0; // Reset stream position
img.StreamSource = ms; // Set image source from stream
img.EndInit();
img.Freeze(); // Make immutable for performance
img.DecodePixelWidth = 150;
img.DecodePixelHeight = 100;
bitmapImage = img; // Overwrite default with extracted cover
});
ms.Dispose(); // Dispose MemoryStream
}
}
catch (Exception tagLibEx)
{
// Catch errors during TagLib processing (e.g., corrupted file, unsupported format)
Console.WriteLine($"Error extracting album art for {path}: {tagLibEx.Message}");
// Consider adding to your specific logger here: log.Error(...)
// bitmapImage remains defaultMusicImage or null if default also failed
}
finally
{
file?.Dispose(); // Ensure TagLib.File is disposed to release file locks
}
}
else if (isFoto) // Logic for Photo files: direct image loading
{
try
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Load full image data
bitmapImage.UriSource = new Uri(path, UriKind.Absolute); // Set URI source
bitmapImage.DecodePixelWidth = 150;
bitmapImage.DecodePixelHeight = 100;
bitmapImage.EndInit();
bitmapImage.Freeze(); // Make immutable
});
}
catch (Exception ex)
{
// Catch errors during photo image loading
Console.WriteLine($"Error loading photo image {path}: {ex.Message}");
// Consider adding to your specific logger here: log.Error(...)
bitmapImage = null; // Set image to null on failure
}
}
else // Logic for Video files: using VideoFrameExtractor
{
// VideoFrameExtractor.GetFrame200 should return null on failure (as previously discussed)
bitmapImage = await Task.Run(() => VideoFrameExtractor.GetFrame200(path));
}
// Create the Item object on the UI thread
await Application.Current.Dispatcher.InvokeAsync(() =>
{
// newItem will have the extracted image, default image, or null if all failed
newItem = new Item(path, type, bitmapImage, isFoto, OnItemPlayButtonClick);
});
itemsBag.Add(newItem); // Add the created item to the thread-safe bag
Application.Current.Dispatcher.Invoke(() => progressScann.UpdateProgress(1)); // Update progress on UI thread
}
catch (Exception ex)
{
// Catch any unexpected exceptions during the processing of a single item
Console.WriteLine($"Unexpected error processing item {path}: {ex.Message}");
// Consider adding to your specific logger here: log.Error(...)
// Create a fallback Item with a null image to ensure the task completes
await Application.Current.Dispatcher.InvokeAsync(() =>
{
newItem = new Item(path, type, null, isFoto, OnItemPlayButtonClick);
});
itemsBag.Add(newItem); // Add this fallback item
Application.Current.Dispatcher.Invoke(() => progressScann.UpdateProgress(1)); // Still update progress
}
finally
{
semaphore.Release(); // Release the semaphore slot regardless of success or failure
}
return newItem; // Return the processed item (can have null image)
}).ToList(); // Convert the collection of tasks to a List<Task<Item>>
// Wait for all individual item processing tasks to complete
await Task.WhenAll(tasks);
// Convert the ConcurrentBag to a List and return
return itemsBag.ToList();
}
#endregion
#region Comunication
public void ReciveErrorLog(string logMessage)
{
log.Error(logMessage);
}
public void PlayVideo(string filePath)
{
if(suportedVidioFiles.Contains(Path.GetExtension(filePath))||suportedPhotoFiles.Contains(Path.GetExtension(filePath)))
VideoView.Visibility = Visibility.Visible;
var media = new Media(_libVLC, filePath, FromType.FromPath);
Console.WriteLine(filePath);
media.ParsedChanged += (sender, args) =>
{
if (args.ParsedStatus == MediaParsedStatus.Done)
{
Dispatcher.Invoke(() =>
{
var duration = media.Duration;
Console.WriteLine(duration);
videoSliderInit(duration / 1000);
title.Text=Path.GetFileNameWithoutExtension(filePath);
});
}
};
media.Parse(MediaParseOptions.ParseLocal);
_mediaPlayer.Play(media);
}
public void setStrings()
{
#region type?
var currentMedia = _mediaPlayer?.Media;
if (currentMedia != null)
{
var mrl = currentMedia.Mrl; // This is the media resource locator
_path = new Uri(mrl).LocalPath;;
_path = stringConversions.ReplaceFirst(_path, '/', '\\');
Console.WriteLine("Now Playing Path: " + _path);
}
if (_mediaPlayer.IsPlaying)
{
_mediaPlayer.Stop();
}
if (suportedMusicFiles.Any(keyword => _path.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
{
_category = "music";
}
else if (suportedPhotoFiles.Any(keyword => _path.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
{
_category = "photos";
}
else
{
if (Regex.IsMatch(_path, @"\b(E|EP|Ep|e|ep)\d+\b", RegexOptions.IgnoreCase))
{
_category = "serie";
}
else
{
_category = "muvie";
}
}
log.Log($"replacing: {_path} from {_category}");
#endregion
}
public void videoArrows(ref Catagory catagory, bool next)
{
catagory = catagory as Catagory;
var items = catagory?.getAllItems();
if (catagory == null || items == null)
{
MessageBox.Show("Category or items are null");
return;
}
int tmp = catagory.contains(_path);
if (tmp == -1 || tmp >= items.Count)
{
MessageBox.Show("Current item not found in category");
return;
}
Media media = null;
int newIndex =0;
if (next)
{
if (tmp + 1 < items.Count)
{
var link = items[tmp + 1]?.getLink();
if (!string.IsNullOrEmpty(link))
media = new Media(_libVLC, link, FromType.FromPath);
}
}
if (!next)
{
newIndex = (tmp - 1 >= 0) ? tmp - 1 : items.Count - 1; // wrap around
var link = items[newIndex]?.getLink();
Console.WriteLine(link);
if (!string.IsNullOrEmpty(link))
media = new Media(_libVLC, link, FromType.FromPath);
}
if (media != null)
{
_mediaPlayer.Play(media);
title.Text = items[newIndex].Name.ToString();
}
else
MessageBox.Show("Could not load media. Media or link is null.");
}
private void Close_Player()
{
_mediaPlayer.Stop();
VideoView.Visibility = Visibility.Collapsed;
}
public void RecivePlaylist(Catagory items)
{
_playlists.SharedRefs.Add(items);
RecivePlaylist();
var writer = new Jason_Writer();
var tmp = new List<locJasonPlaylist>();
int index = 0;
foreach (var VARIABLE in items.getAllItems())
{
tmp.Add(new locJasonPlaylist());
tmp[index].name = VARIABLE.Name;
tmp[index].path = VARIABLE.Path;
tmp[index].type = VARIABLE.Type;
index++;
}
writer.SavePlaylistListAsync(items.name,tmp);
Console.WriteLine("wrote"+items.name);
}
public void RecivePlaylist(Catagory items,bool favorite)
{
if (favorite)
{
Favorites.clear();
foreach (var VARIABLE in items.getAllItems())
{
Favorites.addItem(VARIABLE);
}
}
else
{
foreach (var VARIABLE in _playlists.SharedRefs)
{
if (VARIABLE.name == items.name)
{
VARIABLE.clear();
foreach (var var2 in items.getAllItems())
{
VARIABLE.addItem(var2);
}
break;
}
}
}
RecivePlaylist();
var writer = new Jason_Writer();
var tmp = new List<locJasonPlaylist>();
int index = 0;
foreach (var VARIABLE in items.getAllItems())
{
tmp.Add(new locJasonPlaylist());
tmp[index].name = VARIABLE.Name;
tmp[index].path = VARIABLE.Path;
tmp[index].type = VARIABLE.Type;
index++;
}
writer.SavePlaylistListAsync(items.name,tmp);
Console.WriteLine("faf updated");
if (favorite)
Application.Current.Dispatcher.Invoke(() =>
{
MenueItems(); // safely update UI
});
foreach (var VARIABLE in items.getAllItems())
{
Console.WriteLine(VARIABLE.Name);
}
}
public void RecivePlaylist()
{
popupWindow.Close();
}
private void PopulateAllCategoriesFromPlaylists()
{
PlaylistContentPanel.Children.Clear(); // Optional: clear previous entries
foreach (Catagory category in _playlists.SharedRefs)
{
// Create TextBlock for category name
var textBlock = new TextBlock
{
Text = category.name,
FontSize = 18,
Foreground = Brushes.White,
Margin = new Thickness(0, 10, 0, 5)
};
// Create WrapPanel for items
var wrapPanel = new WrapPanel
{
Margin = new Thickness(0, 0, 0, 10)
};
// Fill WrapPanel with items
PopulatePanelWithItems(category.getAllItems(), wrapPanel);
// Add both to the ScrollViewer's inner panel
PlaylistContentPanel.Children.Add(textBlock);
PlaylistContentPanel.Children.Add(wrapPanel);
}
// Make the ScrollViewer visible
ScrollContentPlaylist.Visibility = Visibility.Visible;
PopulatePlaylistsWithButtons();
}
#region tvRemote
BluetoothServer Server1;
private void InitBluetoothServer()
{
Server1 = new BluetoothServer();
Server1.ClientConnected += () =>
{
Dispatcher.Invoke(() =>
{
MessageBox.Show("🔗 Client connected!");
});
};
Server1.MessageReceived += (msg) =>
{
Dispatcher.BeginInvoke(new Action(()=>
{
//MessageBox.Show($"📨 Message: {msg.Trim()}");
switch (msg.Trim())
{
// Arrow Up
case "↑": // \u2191
var keyEventArgs = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Up)
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs);
break;
// Arrow Down
case "↓": // \u2193
var keyEventArgs2 = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Down)
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs2);
break;
// Arrow Left
case "←": // \u2190
var keyEventArgs3 = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Left)
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs3);
break;
// Arrow Right
case "→": // \u2192
var keyEventArgs4 = new KeyEventArgs(
Keyboard.PrimaryDevice,
PresentationSource.FromVisual(this),
0,
Key.Right)
{
RoutedEvent = Keyboard.PreviewKeyDownEvent
};
this.RaiseEvent(keyEventArgs4);
break;
// Double Left
case "⇚": // \u21DA
var clickEventArgs1 = new RoutedEventArgs(Button.ClickEvent);
prior.RaiseEvent(clickEventArgs1);
break;
// Double Right
case "⇛": // \u21DB
var clickEventArgs2 = new RoutedEventArgs(Button.ClickEvent);
next.RaiseEvent(clickEventArgs2);
break;
// Center button (Dot)
case "●":
Dispatcher.BeginInvoke(new Action(() =>
{
if (_mediaPlayer.IsPlaying)
{
// Simulate pause
OnItemPauseBtn_Click(null, null);
}
else
{
// Simulate item selection
var focused = Keyboard.FocusedElement as Button;
focused?.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}
}));
break;
case "Vol -":
if(_mediaPlayer.Volume>10)
_mediaPlayer.Volume = _mediaPlayer.Volume - 10;
else
_mediaPlayer.Volume = 10;
vol.Value = _mediaPlayer.Volume;
break;
case "Vol +":
if(_mediaPlayer.Volume<90)
_mediaPlayer.Volume = _mediaPlayer.Volume + 10;
else
_mediaPlayer.Volume = 100;
vol.Value = _mediaPlayer.Volume;
break;
case string s when s.StartsWith("Time:"):
string valuePart = s.Substring("Time:".Length);
if (double.TryParse(valuePart, out double timeValue))
{
_mediaPlayer.Time= Convert.ToInt32(valuePart);;
}
else
{
MessageBox.Show("Invalid time format.");
}
break;
default:
MessageBox.Show($"Unrecognized input: '{msg}'");
break;
}
Console.WriteLine(msg+":recived");
}));
// Optional: echo back the message
Server1.SendMessage("Echo: " + msg.Trim());
};
Server1.ErrorOccurred += (err) =>
{
Dispatcher.Invoke(() =>
{
MessageBox.Show($"⚠️ Error: {err}");
});
};
// Start server in background
new Thread(() => Server1.Init())
{
IsBackground = true
}.Start();
}
#endregion
#endregion
#endregion
#region Menue
void MenueItems(ref Catagory catagory)
{
PopulatePanelWithItemsCatagorised(Name_of_Catagory,ref catagory);
}
void MenueItems(ref Catagory catagory,ref Catagory catagory1)
{
PopulatePanelWithItemsCatagorised(Name_of_Catagory,ref catagory);
PopulatePanelWithItemsCatagorised(Name_of_Catagory1,ref catagory1);
}
void MenueItems()
{
PopulatePanelWithItems(Favorites.getAllItems(), Favorites_Home);
PopulatePanelWithItems(Muvie.getAllItems(), Muvies_Home);
PopulatePanelWithItems(Serie.getAllItems(), Series_Home);
PopulatePanelWithItems(Photo.getAllItems(), Photos_Home);
PopulatePanelWithItems(Music.getAllItems(), Music_Home);
}
#endregion
#region PanelPop
private void PopulatePanelWithItemsCatagorised(Panel targetPanel,ref Catagory catagory)
{
if (catagory.name == "Serie")
{
Name_of_Catagory_Text1.Text = catagory.name;
}
else
{
Name_of_Catagory_Text.Text = catagory.name;
}
PopulatePanelWithItems(catagory.getAllItems(),targetPanel);
}
private void PopulatePanelWithItems(List<Item> items, Panel targetPanel)
{
targetPanel.Children.Clear();
byte tmp = 0;
foreach (var item in items)
{
if(tmp >= 10)
break;
if (ScrollContentHome.Visibility == Visibility.Visible)
tmp++;
if (tmp < 10)
{
var name = item.getName();
var image = item.getImage();
var isFoto = item.getType() == "Photo";
var path = item.getLink();
var buttonText = isFoto ? "Show" : "Play";
if (image == null)
continue; // Skip if image is not available
// Container for stacking label, image, and button
var container = new Grid
{
Width = 150,
Height = 120,
Margin = new Thickness(5)
};
// Define rows for layout
container.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); // Image
container.RowDefinitions.Add(new RowDefinition { Height = new GridLength(30) }); // Button
// Image
var imgControl = new System.Windows.Controls.Image
{
Source = image,
Width = 150,
Height = 90,
Stretch = Stretch.UniformToFill
};
Grid.SetRow(imgControl, 0);
container.Children.Add(imgControl);
// Label (overlays top-left, optional)
var label = new Label
{
Content = name,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
Background = new SolidColorBrush(Color.FromArgb(180, 0, 0, 0)),
Foreground = Brushes.White,
Padding = new Thickness(4),
FontSize = 12
};
Grid.SetRow(label, 0);
container.Children.Add(label);
// Button
var btn = new Button
{
Content = buttonText,
Height = 25,
Width = 140,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 0, 0, 5)
};
btn.Click += (s, e) => PlayVideo(path);
Grid.SetRow(btn, 1);
container.Children.Add(btn);
// Add to target UI panel
targetPanel.Children.Add(container);
}
}
}
private void PopulatePlaylistsWithButtons()
{
// Make sure the playlist ScrollViewer is visible
ScrollContentHome.Visibility = Visibility.Collapsed;
ScrollContentCat.Visibility = Visibility.Collapsed;
ScrollContentPlaylist.Visibility = Visibility.Visible;
// Clear previous content
PlaylistContentPanel.Children.Clear();
// Add "New Playlist" button
var newPlaylistBtn = new Button
{
Content = "New Playlist",
Background = Brushes.Black,
Foreground = Brushes.White,
Margin = new Thickness(5)
};
newPlaylistBtn.Click += PlayListButton_Click;
PlaylistContentPanel.Children.Add(newPlaylistBtn);
// Guard: If there are no playlists, show a message
if (_playlists?.SharedRefs == null || !_playlists.SharedRefs.Any())
{
PlaylistContentPanel.Children.Add(new TextBlock
{
Text = "No playlists available.",
Foreground = Brushes.White,
Margin = new Thickness(10)
});
return;
}
// Add playlist sections
foreach (var playlistCategory in _playlists.SharedRefs)
{
var container = new StackPanel
{
Margin = new Thickness(10),
Orientation = Orientation.Vertical,
HorizontalAlignment = HorizontalAlignment.Stretch
};
// Header row with playlist name and ">>" button
var headerRow = new DockPanel
{
LastChildFill = true, // Let the title take remaining space
HorizontalAlignment = HorizontalAlignment.Stretch
};
// Button (goes first, docked right)
var btn = new Button
{
Content = ">>",
Width = 30,
Height = 25,
Margin = new Thickness(10, 0, 0, 0),
VerticalAlignment = VerticalAlignment.Center
};
DockPanel.SetDock(btn, Dock.Right);
btn.Click += (s, e) => ShowFullCategoryView(playlistCategory);
headerRow.Children.Add(btn);
// Playlist name
var title = new TextBlock
{
Text = playlistCategory.name,
FontSize = 18,
FontWeight = FontWeights.Bold,
Foreground = Brushes.White,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(5)
};
headerRow.Children.Add(title);
container.Children.Add(headerRow);
// Preview items in WrapPanel
var wrap = new WrapPanel();
PopulatePanelWithItems(playlistCategory.getAllItems().Take(9).ToList(), wrap);
container.Children.Add(wrap);
PlaylistContentPanel.Children.Add(container);
}
}
private void ShowFullCategoryView(Catagory category)
{
ScrollContentHome.Visibility = Visibility.Collapsed;
ScrollContentPlaylist.Visibility = Visibility.Collapsed;
ScrollContentCat.Visibility = Visibility.Visible;
Name_of_Catagory_Text.Text = category.name;
Name_of_Catagory1.Visibility = Visibility.Collapsed;
PopulatePanelWithItems(category.getAllItems(), Name_of_Catagory);
}
#endregion
#region load saved data
private async void LoadSavedData()
{
try
{
Jason_Writer jason_Writer = new Jason_Writer(); // Uses default "Temp Data" folder
// Load main media lists
var muvies = await jason_Writer.LoadListAsync("Muvies");
var series = await jason_Writer.LoadListAsync("Series");
var photos = await jason_Writer.LoadListAsync("Photos");
var music = await jason_Writer.LoadListAsync("Music");
var favorites = await jason_Writer.LoadListAsync("Favorit");
// Initialize progress bar with total count
progressScann = new ProgressBar("Reading saved data",
muvies.Count + series.Count + photos.Count + music.Count + favorites.Count);
progressScann.Show();
// Create Item lists from JSON
List<Item> MuvieItems = CreateItemsFromJson(muvies);
List<Item> SerieItems = CreateItemsFromJson(series);
List<Item> PhotoItems = CreateItemsFromJson(photos, isPhoto: true);
List<Item> MusicItems = CreateItemsFromJson(music, isPhoto: false, isMusic: true);
Muvie.addItems(MuvieItems);
Serie.addItems(SerieItems);
Photo.addItems(PhotoItems);
Music.addItems(MusicItems);
// Combine all media items for playlist/favorite matching
var allLocs = muvies.Concat(series).Concat(photos).Concat(music).ToList();
// Enrich favorite items by matching them with main list entries
var enrichedFavorites = new List<locJason>();
foreach (var fav in favorites)
{
var match = allLocs.FirstOrDefault(item =>
string.Equals(item.path, fav.path, StringComparison.OrdinalIgnoreCase));
if (match != null)
{
enrichedFavorites.Add(match);
}
else
{
enrichedFavorites.Add(fav); // fallback if not found
Console.WriteLine($"[Warning] Favorite not found in main lists: {fav.path}");
}
}
List<Item> FavoriteItems = CreateItemsFromJson(enrichedFavorites);
Favorites.addItems(FavoriteItems);
// Load playlists
var playlistNames = await jason_Writer.GetSavedPlaylistNamesAsync();
foreach (var playlistName in playlistNames)
{
var playlistEntries = await jason_Writer.LoadPlaylistListAsync(playlistName);
var matchedLocs = new List<locJason>();
foreach (var entry in playlistEntries)
{
var match = allLocs.FirstOrDefault(item =>
string.Equals(item.path, entry.path, StringComparison.OrdinalIgnoreCase));
if (match != null)
{
matchedLocs.Add(match);
}
else
{
Console.WriteLine($"[Warning] Playlist '{playlistName}' contains unknown path: {entry.path}");
}
}
List<Item> PlaylistItems = CreateItemsFromJson(matchedLocs);
var playlistCategory = new Catagory(playlistName);
playlistCategory.addItems(PlaylistItems);
_playlists.SharedRefs.Add(playlistCategory);
}
MenueItems(); // Refresh the UI
progressScann.Hide();
log.Log("Loaded saved data from JSON.");
}
catch (Exception ex)
{
MessageBox.Show($"Failed to load saved data:\n{ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
log.Error($"LoadSavedData error: {ex}");
}
}
private List<Item> CreateItemsFromJson(List<locJason> jsonItems)
{
return CreateItemsFromJson(jsonItems, isPhoto: false, isMusic: false);
}
private List<Item> CreateItemsFromJson(List<locJason> jsonItems, bool isPhoto)
{
return CreateItemsFromJson(jsonItems, isPhoto, isMusic: false);
}
private List<Item> CreateItemsFromJson(List<locJason> jsonItems, bool isPhoto, bool isMusic)
{
List<Item> items = new List<Item>();
JasonToString jasonToString = new JasonToString();
foreach (var loc in jsonItems)
{
BitmapImage bitmapImage = null;
try
{
if (!string.IsNullOrEmpty(loc.imageData))
{
bitmapImage = BitmapConversions.BitmapToBitmapImage(
jasonToString.Base64StringToBitmap(loc.imageData)
);
}
else if (isMusic)
{
string defaultImagePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Pics", "MusicD.jpeg");
if (File.Exists(defaultImagePath))
{
bitmapImage = new BitmapImage(new Uri(defaultImagePath));
bitmapImage.Freeze();
}
}
else if (isPhoto && File.Exists(loc.path))
{
bitmapImage = new BitmapImage(new Uri(loc.path));
bitmapImage.Freeze();
}
}
catch
{
// Handle any corrupt or missing image cases
}
items.Add(new Item(loc.path, loc.type, bitmapImage, isPhoto, OnItemPlayButtonClick));
Application.Current.Dispatcher.Invoke(() =>
{
progressScann.UpdateProgress(1);
});
}
return items;
}
#endregion
#region sliders
private void RangeBase_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (sender is Slider&&_mediaPlayer != null)
{
Slider slider = sender as Slider;
int value = Convert.ToInt32(slider.Value);
_mediaPlayer.Volume = value;
Server1.SendMessage("Vol=" + value);
log.Log("VolumeChanged");
}
}
#endregion
#region progress slider
private void ItemProgress_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (itemProgress.IsMouseOver&&(itemProgress.Value>_mediaPlayer.Time/1000+2||itemProgress.Value<_mediaPlayer.Time/1000-2))
{
ProgressBarValueChange();
}
}
private void ItemProgress_OnValueChangedMil(long timeStap)
{
itemProgress.Value = timeStap/1000;
itemProgressVisual.Value = itemProgress.Value;
if(Server1!=null)
Server1.SendMessage("Progress="+itemProgress.Value);
}
private void ProgressBarValueChange()
{
itemProgressVisual.Value = itemProgress.Value;
if(itemProgress.IsMouseOver)
_mediaPlayer.Time = Convert.ToInt64(itemProgress.Value) * 1000; // convert to milliseconds
}
private void videoSliderInit(long timeStap)
{
itemProgress.Maximum = timeStap;
itemProgressVisual.Maximum = timeStap;
itemProgress.Minimum = 0;
itemProgressVisual.Minimum = 0;
StartProgressTimer();
Console.WriteLine(timeStap);
}
private void ItemProgress_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var slider = sender as Slider;
if (slider == null) return;
Point position = e.GetPosition(slider);
double relativePosition = position.X / slider.ActualWidth;
relativePosition = Math.Max(0, Math.Min(1, relativePosition));
double newValue = slider.Minimum + (relativePosition * (slider.Maximum - slider.Minimum));
slider.Value = newValue;
ProgressBarValueChange();
}
private void ProgressTimer_Tick(object sender, EventArgs e)
{
if (!_mediaPlayer.IsPlaying)
{
StopProgressTimer();
}
// Get current timestamp from media player in seconds
long currentTimeInSeconds = _mediaPlayer.Time;
// Update progress bar value
ItemProgress_OnValueChangedMil(currentTimeInSeconds);
}
private void StartProgressTimer()
{
if (!_progressTimer.IsEnabled)
_progressTimer.Start();
}
private void StopProgressTimer()
{
if (_progressTimer.IsEnabled)
_progressTimer.Stop();
}
private void Key_Up(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Escape:
var result = MessageBox.Show(
"Do you want to quit?",
"Exit",
MessageBoxButton.YesNo,
MessageBoxImage.Question
);
if (result == MessageBoxResult.Yes)
{
Application.Current.Shutdown();
}
break;
}
if (VideoView.IsVisible)
{
switch (e.Key)
{
case Key.Left:_mediaPlayer.Time=_mediaPlayer.Time-10000;break;
case Key.Right:_mediaPlayer.Time=_mediaPlayer.Time+10000;break;
case Key.Space:
if (_mediaPlayer.IsPlaying)
_mediaPlayer.Pause();
else
_mediaPlayer.Play();
break;
}
}
else
{
}
}
#endregion
#region arrow nav
private void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.FocusedElement is not DependencyObject focusedElement)
return;
var wrapPanel = FindAncestor<WrapPanel>(focusedElement);
var stackPanel = FindAncestor<StackPanel>(focusedElement);
if (wrapPanel != null && wrapPanel.Children.Count > 0)
{
int focusedIndex = FindFocusedIndexInWrapPanel(wrapPanel, focusedElement);
if (focusedIndex == -1)
return;
int columns = CalculateColumns(wrapPanel);
switch (e.Key)
{
case Key.Left:
e.Handled = true;
if (IsLeftmostInRow(focusedIndex, columns))
{
FirstCat?.Focus();
}
else
{
MoveFocusInWrapPanel(wrapPanel, focusedIndex - 1);
}
break;
case Key.Right:
e.Handled = true;
MoveFocusInWrapPanel(wrapPanel, focusedIndex + 1);
break;
case Key.Up:
e.Handled = true;
if (focusedIndex < columns)
{
if (stackPanel != null)
{
int currentWrapPanelIndex = stackPanel.Children.IndexOf(wrapPanel);
if (currentWrapPanelIndex > 0)
{
for (int i = currentWrapPanelIndex - 1; i >= 0; i--)
{
if (stackPanel.Children[i] is WrapPanel prevWrapPanel && prevWrapPanel.Children.Count > 0)
{
int targetIndexInPrev = Math.Min(prevWrapPanel.Children.Count - 1, focusedIndex + ((prevWrapPanel.Children.Count / columns) * columns));
MoveFocusInWrapPanel(prevWrapPanel, targetIndexInPrev);
return;
}
}
}
}
}
MoveFocusInWrapPanel(wrapPanel, focusedIndex - columns);
break;
case Key.Down:
e.Handled = true;
if (focusedIndex >= wrapPanel.Children.Count - columns)
{
if (stackPanel != null)
{
int currentWrapPanelIndex = stackPanel.Children.IndexOf(wrapPanel);
if (currentWrapPanelIndex != -1 && currentWrapPanelIndex < stackPanel.Children.Count - 1)
{
for (int i = currentWrapPanelIndex + 1; i < stackPanel.Children.Count; i++)
{
if (stackPanel.Children[i] is WrapPanel nextWrapPanel && nextWrapPanel.Children.Count > 0)
{
int targetIndexInNext = Math.Min(columns - 1, nextWrapPanel.Children.Count - 1);
MoveFocusInWrapPanel(nextWrapPanel, targetIndexInNext);
return;
}
}
}
}
}
MoveFocusInWrapPanel(wrapPanel, focusedIndex + columns);
break;
}
}
else if (FirstCat.IsFocused && e.Key == Key.Right)
{
e.Handled = true; // Always handle the key if we are trying to move from FirstCat
var firstFocusable = FindFirstFocusableInVisibleWrapPanel();
if (firstFocusable != null)
{
firstFocusable.Focus();
}
else
{
// Fallback: Wenn kein spezifisches fokussierbares Element gefunden wurde, versuchen Sie, den ScrollViewer selbst zu fokussieren
if (ScrollContentHome.Visibility == Visibility.Visible) ScrollContentHome.Focus();
else if (ScrollContentCat.Visibility == Visibility.Visible) ScrollContentCat.Focus();
else if (ScrollContentPlaylist.Visibility == Visibility.Visible) ScrollContentPlaylist.Focus();
}
}
else if (catPan.IsAncestorOf(Keyboard.FocusedElement as DependencyObject) && e.Key == Key.Down)
{
e.Handled = true;
var buttons = catPan.Children.OfType<Button>().ToList();
var focused = Keyboard.FocusedElement as Button;
int index = buttons.IndexOf(focused);
if (index != -1 && index < buttons.Count - 1)
{
Dispatcher.BeginInvoke(new Action(() =>
{
buttons[index + 1].Focus();
}));
}
}
else if (catPan.IsAncestorOf(Keyboard.FocusedElement as DependencyObject) && e.Key == Key.Up)
{
e.Handled = true;
var buttons = catPan.Children.OfType<Button>().ToList();
var focused = Keyboard.FocusedElement as Button;
int index = buttons.IndexOf(focused);
if (index > 0)
{
Dispatcher.BeginInvoke(new Action(() =>
{
buttons[index - 1].Focus();
}));
}
}
}
private Control FindFirstFocusableInVisibleWrapPanel()
{
ScrollViewer currentScrollViewer = null;
if (ScrollContentHome.Visibility == Visibility.Visible)
{
currentScrollViewer = ScrollContentHome;
}
else if (ScrollContentCat.Visibility == Visibility.Visible)
{
currentScrollViewer = ScrollContentCat;
}
else if (ScrollContentPlaylist.Visibility == Visibility.Visible)
{
currentScrollViewer = ScrollContentPlaylist;
}
else
{
Console.WriteLine("No visible ScrollViewer found");
return null;
}
Panel mainPanel = currentScrollViewer.Content as Panel;
if (mainPanel != null)
{
for (int i = 0; i < mainPanel.Children.Count; i++)
{
var child = mainPanel.Children[i];
WrapPanel wp = child as WrapPanel;
if (wp != null && wp.Visibility == Visibility.Visible && wp.Children.Count > 0)
{
Control firstFocusable = FindFirstFocusableChild(wp);
if (firstFocusable != null)
{
return firstFocusable;
}
}
}
}
else
{
Console.WriteLine("ScrollViewer content is not a Panel");
}
Control fallback = FindFirstFocusableChild(currentScrollViewer.Content as DependencyObject);
if (fallback != null)
return fallback;
return null;
}
private int CalculateColumns(WrapPanel panel)
{
if (panel.Children.Count == 0) return 1;
var firstChild = panel.Children[0] as FrameworkElement;
if (firstChild == null) return 1;
double itemWidth = firstChild.ActualWidth + firstChild.Margin.Left + firstChild.Margin.Right;
if (itemWidth <= 1) itemWidth = 150;
double panelWidth = panel.ActualWidth;
int columns = Math.Max(1, (int)(panelWidth / itemWidth));
return columns;
}
private bool IsLeftmostInRow(int index, int columns) => (index % columns) == 0;
private void MoveFocusInWrapPanel(WrapPanel panel, int index)
{
if (index < 0) index = 0;
if (index >= panel.Children.Count) index = panel.Children.Count - 1;
if (panel.Children[index] is Control ctrl && ctrl.Focusable)
{
ctrl.Focus();
ctrl.BringIntoView();
}
else if (panel.Children[index] is Panel container)
{
var focusable = FindFirstFocusableChild(container);
if (focusable != null)
{
focusable.Focus();
focusable.BringIntoView();
}
}
}
private int FindFocusedIndexInWrapPanel(WrapPanel panel, DependencyObject focusedElement)
{
for (int i = 0; i < panel.Children.Count; i++)
{
var container = panel.Children[i];
if (IsOrContains(container, focusedElement))
return i;
}
return -1;
}
private bool IsOrContains(DependencyObject parent, DependencyObject child)
{
if (parent == child) return true;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var sub = VisualTreeHelper.GetChild(parent, i);
if (IsOrContains(sub, child))
return true;
}
return false;
}
private Control FindFirstFocusableChild(DependencyObject parent)
{
if (parent is Control c && c.Focusable)
return c;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var found = FindFirstFocusableChild(VisualTreeHelper.GetChild(parent, i));
if (found != null)
return found;
}
return null;
}
private static T FindAncestor<T>(DependencyObject child) where T : DependencyObject
{
DependencyObject current = child;
while (current != null)
{
if (current is T match)
return match;
current = VisualTreeHelper.GetParent(current);
}
return null;
}
public void SetInitialFocusForCurrentView()
{
this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input, new Action(() =>
{
Control firstFocusable = FindFirstFocusableInVisibleWrapPanel();
if (firstFocusable != null)
{
firstFocusable.Focus();
}
else
{
if (ScrollContentHome.Visibility == Visibility.Visible) ScrollContentHome.Focus();
else if (ScrollContentCat.Visibility == Visibility.Visible) ScrollContentCat.Focus();
else if (ScrollContentPlaylist.Visibility == Visibility.Visible) ScrollContentPlaylist.Focus();
}
}));
}
#endregion
}