FileManager: Add "Audio" tab to File Properties window

This commit is contained in:
Sam Atkins 2023-07-19 10:44:32 +01:00 committed by Sam Atkins
parent 0a9fa85e86
commit 91b677c81e
4 changed files with 293 additions and 1 deletions

View file

@ -7,6 +7,7 @@ serenity_component(
compile_gml(FileManagerWindow.gml FileManagerWindowGML.h file_manager_window_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)
set(SOURCES
@ -21,8 +22,9 @@ set(SOURCES
set(GENERATED_SOURCES
FileManagerWindowGML.h
FileOperationProgressGML.h
PropertiesWindowAudioTabGML.h
PropertiesWindowGeneralTabGML.h
)
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)

View file

@ -1,6 +1,7 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022-2023, the SerenityOS developers.
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -9,7 +10,9 @@
#include <AK/LexicalPath.h>
#include <AK/NumberFormat.h>
#include <Applications/FileManager/DirectoryView.h>
#include <Applications/FileManager/PropertiesWindowAudioTabGML.h>
#include <Applications/FileManager/PropertiesWindowGeneralTabGML.h>
#include <LibAudio/Loader.h>
#include <LibCore/Directory.h>
#include <LibCore/System.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>());
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>());
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 {};
}
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()
{
m_icon->set_bitmap(GUI::FileIconProvider::icon_for_path(make_full_path(m_name), m_mode).bitmap_for_size(32));

View file

@ -29,6 +29,8 @@ private:
PropertiesWindow(DeprecatedString const& path, Window* parent = nullptr);
ErrorOr<void> create_widgets(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 {
mode_t read;

View 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"
}
}
}
}