ladybird/Libraries/LibWeb/HTML/MediaControls.h
Zaggy1024 63113cf5c9 Meta+LibWeb: Use a code generator to create the media controls' DOM
Instead of manually writing code to instantiate DOM elements in
MediaControls.cpp, use a Python script to generate a separate C++
struct to create and store the DOM elements.

The generator reads an HTML file and the HTML/SVG tags/attributes
headers to create C++ source that instantiates the DOM elements.

To enable embedding of stylesheets in shadow DOM, the generator
replaces `<link rel="stylesheet">` elements with plain `<style>`
elements containing the source from the linked stylesheet.

Elements that should be stored in the resulting struct should be marked
have the `data-name` attribute, which will be converted to snake_case
and used as the public field's name.

Optional elements can be marked with a 'data-option' attribute. Each
unique option value will be converted to PascalCase and added to a
bitwise enum named `Options` nested within the struct. Optional
elements and all their children will not be instantiated unless their
option is set in the constructor argument.

The MediaControls class stores the generated MediaControlsDOM struct
and sets up event handlers to implement user interactions.
2026-03-05 02:28:47 -06:00

98 lines
2.8 KiB
C++

/*
* Copyright (c) 2026, Gregory Bertilson <gregory@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
#include <AK/Optional.h>
#include <AK/Vector.h>
#include <LibCore/Timer.h>
#include <LibGC/Weak.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/MediaControlsDOM.h>
namespace Web::HTML {
class HTMLMediaElement;
class MediaControls {
public:
explicit MediaControls(HTMLMediaElement&);
~MediaControls();
private:
void create_shadow_tree();
enum class ListenOnce : bool {
No,
Yes,
};
template<typename T, CallableAs<bool, T&> Handler>
GC::Ref<DOM::IDLEventListener> add_event_listener(JS::Realm&, DOM::EventTarget&, FlyString const& event_name, ListenOnce, Handler);
template<CallableAs<bool> Handler>
GC::Ref<DOM::IDLEventListener> add_event_listener(JS::Realm&, DOM::EventTarget&, FlyString const& event_nam, Handler);
template<CallableAs<bool, UIEvents::MouseEvent const&> Handler>
GC::Ref<DOM::IDLEventListener> add_event_listener(JS::Realm&, DOM::EventTarget&, FlyString const& event_name, Handler);
template<CallableAs<bool, UIEvents::MouseEvent const&> Handler>
GC::Ref<DOM::IDLEventListener> add_event_listener(JS::Realm&, DOM::EventTarget&, FlyString const& event_name, ListenOnce, Handler);
template<CallableAs<bool, UIEvents::KeyboardEvent const&> Handler>
GC::Ref<DOM::IDLEventListener> add_event_listener(JS::Realm&, DOM::EventTarget&, FlyString const& event_name, Handler);
void remove_event_listeners();
void set_up_event_listeners();
void toggle_playback();
void set_current_time(double);
void set_volume(double);
void toggle_mute();
void toggle_fullscreen();
void update_play_pause_icon();
void update_timeline();
void update_timestamp();
void update_volume_and_mute_indicator();
void update_fullscreen_icon();
void update_placeholder_visibility();
bool should_show_placeholder() const;
void show_controls();
void hide_controls();
GC::Weak<HTMLMediaElement> m_media_element;
Optional<MediaControlsDOM> m_dom;
struct RegisteredEventListener {
GC::Weak<DOM::EventTarget> target;
FlyString event_name;
GC::Weak<DOM::IDLEventListener> listener;
};
Vector<RegisteredEventListener> m_registered_event_listeners;
enum class Scrubbing : u8 {
No,
WhilePaused,
WhilePlaying,
};
Scrubbing m_scrubbing_timeline { Scrubbing::No };
bool m_scrubbing_volume { false };
bool m_hovering_controls { false };
RefPtr<Core::Timer> m_hover_timer;
bool m_had_audio { true };
bool m_was_muted { false };
enum class MuteIconState : u8 {
Empty,
Low,
High,
};
MuteIconState m_mute_icon_state { MuteIconState::Empty };
};
}