mirror of
https://github.com/RGBCube/serenity
synced 2025-05-17 19:15:08 +00:00
FileManager: Add "Audio" tab to File Properties window
This commit is contained in:
parent
0a9fa85e86
commit
91b677c81e
4 changed files with 293 additions and 1 deletions
|
@ -7,6 +7,7 @@ serenity_component(
|
||||||
|
|
||||||
compile_gml(FileManagerWindow.gml FileManagerWindowGML.h file_manager_window_gml)
|
compile_gml(FileManagerWindow.gml FileManagerWindowGML.h file_manager_window_gml)
|
||||||
compile_gml(FileOperationProgress.gml FileOperationProgressGML.h file_operation_progress_gml)
|
compile_gml(FileOperationProgress.gml FileOperationProgressGML.h file_operation_progress_gml)
|
||||||
|
compile_gml(PropertiesWindowAudioTab.gml PropertiesWindowAudioTabGML.h properties_window_audio_tab_gml)
|
||||||
compile_gml(PropertiesWindowGeneralTab.gml PropertiesWindowGeneralTabGML.h properties_window_general_tab_gml)
|
compile_gml(PropertiesWindowGeneralTab.gml PropertiesWindowGeneralTabGML.h properties_window_general_tab_gml)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
|
@ -21,8 +22,9 @@ set(SOURCES
|
||||||
set(GENERATED_SOURCES
|
set(GENERATED_SOURCES
|
||||||
FileManagerWindowGML.h
|
FileManagerWindowGML.h
|
||||||
FileOperationProgressGML.h
|
FileOperationProgressGML.h
|
||||||
|
PropertiesWindowAudioTabGML.h
|
||||||
PropertiesWindowGeneralTabGML.h
|
PropertiesWindowGeneralTabGML.h
|
||||||
)
|
)
|
||||||
|
|
||||||
serenity_app(FileManager ICON app-file-manager)
|
serenity_app(FileManager ICON app-file-manager)
|
||||||
target_link_libraries(FileManager PRIVATE LibCore LibFileSystem LibGfx LibGUI LibDesktop LibConfig LibMain LibThreading)
|
target_link_libraries(FileManager PRIVATE LibAudio LibCore LibFileSystem LibGfx LibGUI LibDesktop LibConfig LibMain LibThreading)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2022-2023, the SerenityOS developers.
|
* Copyright (c) 2022-2023, the SerenityOS developers.
|
||||||
|
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -9,7 +10,9 @@
|
||||||
#include <AK/LexicalPath.h>
|
#include <AK/LexicalPath.h>
|
||||||
#include <AK/NumberFormat.h>
|
#include <AK/NumberFormat.h>
|
||||||
#include <Applications/FileManager/DirectoryView.h>
|
#include <Applications/FileManager/DirectoryView.h>
|
||||||
|
#include <Applications/FileManager/PropertiesWindowAudioTabGML.h>
|
||||||
#include <Applications/FileManager/PropertiesWindowGeneralTabGML.h>
|
#include <Applications/FileManager/PropertiesWindowGeneralTabGML.h>
|
||||||
|
#include <LibAudio/Loader.h>
|
||||||
#include <LibCore/Directory.h>
|
#include <LibCore/Directory.h>
|
||||||
#include <LibCore/System.h>
|
#include <LibCore/System.h>
|
||||||
#include <LibDesktop/Launcher.h>
|
#include <LibDesktop/Launcher.h>
|
||||||
|
@ -58,6 +61,7 @@ ErrorOr<void> PropertiesWindow::create_widgets(bool disable_rename)
|
||||||
|
|
||||||
auto tab_widget = TRY(main_widget->try_add<GUI::TabWidget>());
|
auto tab_widget = TRY(main_widget->try_add<GUI::TabWidget>());
|
||||||
TRY(create_general_tab(tab_widget, disable_rename));
|
TRY(create_general_tab(tab_widget, disable_rename));
|
||||||
|
TRY(create_file_type_specific_tabs(tab_widget));
|
||||||
|
|
||||||
auto button_widget = TRY(main_widget->try_add<GUI::Widget>());
|
auto button_widget = TRY(main_widget->try_add<GUI::Widget>());
|
||||||
TRY(button_widget->try_set_layout<GUI::HorizontalBoxLayout>(GUI::Margins {}, 5));
|
TRY(button_widget->try_set_layout<GUI::HorizontalBoxLayout>(GUI::Margins {}, 5));
|
||||||
|
@ -196,6 +200,62 @@ ErrorOr<void> PropertiesWindow::create_general_tab(GUI::TabWidget& tab_widget, b
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> PropertiesWindow::create_file_type_specific_tabs(GUI::TabWidget& tab_widget)
|
||||||
|
{
|
||||||
|
auto mapped_file_or_error = Core::MappedFile::map(m_path);
|
||||||
|
if (mapped_file_or_error.is_error()) {
|
||||||
|
warnln("{}: {}", m_path, mapped_file_or_error.release_error());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto mapped_file = mapped_file_or_error.release_value();
|
||||||
|
|
||||||
|
auto file_name_guess = Core::guess_mime_type_based_on_filename(m_path);
|
||||||
|
auto mime_type = Core::guess_mime_type_based_on_sniffed_bytes(mapped_file->bytes()).value_or(file_name_guess);
|
||||||
|
|
||||||
|
if (mime_type.starts_with("audio/"sv))
|
||||||
|
return create_audio_tab(tab_widget, move(mapped_file));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> PropertiesWindow::create_audio_tab(GUI::TabWidget& tab_widget, NonnullRefPtr<Core::MappedFile> mapped_file)
|
||||||
|
{
|
||||||
|
auto loader_or_error = Audio::Loader::create(mapped_file->bytes());
|
||||||
|
if (loader_or_error.is_error()) {
|
||||||
|
warnln("Failed to open '{}': {}", m_path, loader_or_error.release_error());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto loader = loader_or_error.release_value();
|
||||||
|
|
||||||
|
auto tab = TRY(tab_widget.try_add_tab<GUI::Widget>("Audio"_short_string));
|
||||||
|
TRY(tab->load_from_gml(properties_window_audio_tab_gml));
|
||||||
|
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_type")->set_text(TRY(String::from_deprecated_string(loader->format_name())));
|
||||||
|
auto duration_seconds = loader->total_samples() / loader->sample_rate();
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_duration")->set_text(TRY(String::from_deprecated_string(human_readable_digital_time(duration_seconds))));
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_sample_rate")->set_text(TRY(String::formatted("{} Hz", loader->sample_rate())));
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_format")->set_text(TRY(String::formatted("{}-bit", loader->bits_per_sample())));
|
||||||
|
|
||||||
|
auto channel_count = loader->num_channels();
|
||||||
|
String channels_string;
|
||||||
|
if (channel_count == 1 || channel_count == 2) {
|
||||||
|
channels_string = TRY(String::formatted("{} ({})", channel_count, channel_count == 1 ? "Mono"sv : "Stereo"sv));
|
||||||
|
} else {
|
||||||
|
channels_string = TRY(String::number(channel_count));
|
||||||
|
}
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_channels")->set_text(channels_string);
|
||||||
|
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_title")->set_text(loader->metadata().title.value_or({}));
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_artists")->set_text(TRY(loader->metadata().all_artists()).value_or({}));
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_album")->set_text(loader->metadata().album.value_or({}));
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_track_number")
|
||||||
|
->set_text(TRY(loader->metadata().track_number.map([](auto number) { return String::number(number); })).value_or({}));
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_genre")->set_text(loader->metadata().genre.value_or({}));
|
||||||
|
tab->find_descendant_of_type_named<GUI::Label>("audio_comment")->set_text(loader->metadata().comment.value_or({}));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void PropertiesWindow::update()
|
void PropertiesWindow::update()
|
||||||
{
|
{
|
||||||
m_icon->set_bitmap(GUI::FileIconProvider::icon_for_path(make_full_path(m_name), m_mode).bitmap_for_size(32));
|
m_icon->set_bitmap(GUI::FileIconProvider::icon_for_path(make_full_path(m_name), m_mode).bitmap_for_size(32));
|
||||||
|
|
|
@ -29,6 +29,8 @@ private:
|
||||||
PropertiesWindow(DeprecatedString const& path, Window* parent = nullptr);
|
PropertiesWindow(DeprecatedString const& path, Window* parent = nullptr);
|
||||||
ErrorOr<void> create_widgets(bool disable_rename);
|
ErrorOr<void> create_widgets(bool disable_rename);
|
||||||
ErrorOr<void> create_general_tab(GUI::TabWidget&, bool disable_rename);
|
ErrorOr<void> create_general_tab(GUI::TabWidget&, bool disable_rename);
|
||||||
|
ErrorOr<void> create_file_type_specific_tabs(GUI::TabWidget&);
|
||||||
|
ErrorOr<void> create_audio_tab(GUI::TabWidget&, NonnullRefPtr<Core::MappedFile>);
|
||||||
|
|
||||||
struct PermissionMasks {
|
struct PermissionMasks {
|
||||||
mode_t read;
|
mode_t read;
|
||||||
|
|
228
Userland/Applications/FileManager/PropertiesWindowAudioTab.gml
Normal file
228
Userland/Applications/FileManager/PropertiesWindowAudioTab.gml
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
@GUI::Widget {
|
||||||
|
layout: @GUI::VerticalBoxLayout {
|
||||||
|
margins: [8]
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::GroupBox {
|
||||||
|
title: "Audio"
|
||||||
|
preferred_height: "shrink"
|
||||||
|
layout: @GUI::VerticalBoxLayout {
|
||||||
|
margins: [12, 8, 0]
|
||||||
|
spacing: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Type:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_type"
|
||||||
|
text: "MP3"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Duration:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_duration"
|
||||||
|
text: "3:21"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Sample rate:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_sample_rate"
|
||||||
|
text: "44100 Hz"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Format:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_format"
|
||||||
|
text: "16-bit"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Channels:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_channels"
|
||||||
|
text: "2 (Stereo)"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::GroupBox {
|
||||||
|
title: "Track"
|
||||||
|
layout: @GUI::VerticalBoxLayout {
|
||||||
|
margins: [12, 8, 0]
|
||||||
|
spacing: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
preferred_height: "shrink"
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Title:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_title"
|
||||||
|
text: "Ana Ng"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
preferred_height: "shrink"
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Artist(s):"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_artists"
|
||||||
|
text: "They Might Be Giants"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
preferred_height: "shrink"
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Album:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_album"
|
||||||
|
text: "Lincoln"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
preferred_height: "shrink"
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Track number:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_track_number"
|
||||||
|
text: "1"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
preferred_height: "shrink"
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Genre:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
name: "audio_genre"
|
||||||
|
text: "Alternative"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
preferred_height: "grow"
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Comment:"
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
fixed_width: 80
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
preferred_height: "grow"
|
||||||
|
name: "audio_comment"
|
||||||
|
text: "Ana Ng and I are getting old and we still haven't walked in the glow of each other's majestic presence."
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue