xtc/src/gui/sequencer_widget.hpp

173 lines
5.5 KiB
C++
Raw Normal View History

#ifndef XTC_SEQUENCER_WIDGET_HPP
#define XTC_SEQUENCER_WIDGET_HPP
#include <FL/Fl_Widget.H>
#include "xtc/xtc_audio.h"
#include <memory>
#include <string>
#include <vector>
2026-10-27 01:37:21 +01:00
class SequencerWidget : public Fl_Widget {
2026-10-27 01:37:21 +01:00
public:
/**
* @brief Prepares a model edit using the widget's opaque callback context.
*
* @return XTC_OK to allow the edit, or an error to cancel it.
*/
xtc_result duplicate_selection();
xtc_result split_selection();
xtc_result audio_properties(float gain, uint64_t fade_in, uint64_t fade_out);
void audio_properties_dialog();
void refresh_waveforms();
size_t waveform_count() const { return waves_.size(); }
xtc_result rename_selection(const char *name);
xtc_result move_track(int direction);
xtc_result grid(uint64_t ticks);
uint64_t grid() const { return grid_; }
using BeforeEdit = xtc_result (*)(void *);
BeforeEdit before_edit = nullptr;
2026-10-27 01:37:21 +01:00
/**
* @brief Returns the selected track identifier, or UINT64_MAX when none is selected.
* @return Stable track identifier, or UINT64_MAX if no track is selected.
*/
2026-10-27 01:37:21 +01:00
uint64_t selected_track() const
{
return track_;
}
/**
* @brief Returns the selected clip identifier, or UINT64_MAX when none is selected.
* @return Stable clip identifier, or UINT64_MAX if no clip is selected.
*/
2026-10-27 01:37:21 +01:00
uint64_t selected_clip() const
{
return clip_;
}
/**
* @brief Requests a transport seek using an opaque context and musical tick.
*
* @return XTC_OK on success, or a transport error.
*/
using Seek = xtc_result (*)(void *, uint64_t);
/**
* @brief Constructs the timeline widget with a borrowed project and seek callback.
*
* @param x Left coordinate in pixels.
* @param y Top coordinate in pixels.
* @param project Borrowed project; must outlive the widget's use of it.
* @param seek Callback used to request a transport seek.
* @param context Opaque callback context.
*/
SequencerWidget(int x, int y, xtc_project *project, Seek seek, void *context);
/**
* @brief Replaces the borrowed project and refreshes the widget's displayed state.
*
* @param project Borrowed project; must outlive the widget's use of it.
* @param reset Whether to clear selection, cursor, and drag state.
*/
void set_project(xtc_project *project, bool reset = true);
2026-10-27 01:37:21 +01:00
/**
* @brief Returns the timeline cursor position in musical ticks.
* @return Cursor position in musical ticks.
*/
2026-10-27 01:37:21 +01:00
uint64_t position() const
{
return cursor_;
}
/**
* @brief Moves the timeline cursor to the supplied musical tick and requests a redraw.
*
* @param tick Musical position in ticks.
*/
2026-10-27 01:37:21 +01:00
void position(uint64_t tick)
{
cursor_ = tick;
redraw();
}
/**
* @brief Creates and selects a track through the arrangement edit hook.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result add_track();
/**
* @brief Creates and selects a one-bar silent region at the timeline cursor.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result add_region();
/**
* @brief Creates and selects a one-bar MIDI clip at the timeline cursor.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result add_midi_clip();
/**
* @brief Removes the selected clip, or the selected track when no clip is selected.
* @return XTC_OK on success, or an error code describing the failure.
*/
xtc_result delete_selection();
/**
* @brief Adjusts timeline zoom within its supported bounds and updates the scroll extent.
*
* @param direction Zoom direction; positive zooms in, nonpositive zooms out.
*/
void zoom(int direction);
2026-10-27 01:37:21 +01:00
/**
* @brief Returns the borrowed project currently displayed by the widget.
* @return Borrowed project pointer.
*/
2026-10-27 01:37:21 +01:00
xtc_project *project() const
{
return project_;
}
/**
* @brief Handles FLTK input events and delegates unhandled events to the base widget.
*
* @param event FLTK event code.
* @return Nonzero if the event was handled, zero otherwise.
*/
int handle(int event) override;
2026-10-27 01:37:21 +01:00
protected:
/**
* @brief Draws the widget's current model, selection, and interaction feedback.
*/
void draw() override;
2026-10-27 01:37:21 +01:00
private:
struct Wave {
std::string path;
std::unique_ptr<xtc_waveform, decltype(&xtc_waveform_destroy)> data{nullptr, xtc_waveform_destroy};
};
std::vector<Wave> waves_;
void draw_waveform(const xtc_clip_info &clip, int left, int top);
xtc_project *project_;
Seek seek_;
void *context_;
uint64_t track_ = UINT64_MAX, clip_ = UINT64_MAX, cursor_ = 0, grid_ = 240;
int scale_ = 48, drag_x_ = 0, mode_ = 0;
xtc_clip_info original_{};
/**
* @brief Converts a screen x coordinate to a bounded timeline tick snapped to a sixteenth note.
*
* @param px Screen x coordinate in pixels.
* @return Snapped tick within the supported project range.
*/
uint64_t tick(int px) const;
/**
* @brief Reports an edit result, refreshes the widget, and notifies listeners on success.
*
* @param result Result of the attempted operation.
*/
void changed(xtc_result result);
/**
* @brief Resizes the timeline's scrollable area to fit its tracks and clip ends.
*/
void extent();
};
#endif