Skip to content

Commit

Permalink
[UI] Add undo & redo support for clip deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
native-m committed Sep 1, 2024
1 parent 5fb29ce commit afcfe6e
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 31 deletions.
20 changes: 13 additions & 7 deletions src/engine/track.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,24 +93,30 @@ void Track::set_mute(bool mute) {
}

Clip* Track::add_audio_clip(const std::string& name, double min_time, double max_time,
const AudioClip& clip_info, double beat_duration) {
double start_offset, const AudioClip& clip_info, double beat_duration,
bool active) {
Clip* clip = (Clip*)clip_allocator.allocate();
if (!clip)
return nullptr;
new (clip) Clip(name, color, min_time, max_time);
clip->init_as_audio_clip(clip_info);
clip->start_offset = start_offset;
clip->active.store(active, std::memory_order_release);
clips.push_back(clip);
update(clip, beat_duration);
return clip;
}

Clip* Track::add_midi_clip(const std::string& name, double min_time, double max_time,
const MidiClip& clip_info, double beat_duration) {
double start_offset, const MidiClip& clip_info, double beat_duration,
bool active) {
Clip* clip = (Clip*)clip_allocator.allocate();
if (!clip)
return nullptr;
new (clip) Clip(name, color, min_time, max_time);
clip->init_as_midi_clip(clip_info);
clip->start_offset = start_offset;
clip->active.store(active, std::memory_order_release);
clips.push_back(clip);
update(clip, beat_duration);
return clip;
Expand Down Expand Up @@ -304,8 +310,8 @@ void Track::process_event(uint32_t buffer_offset, double time_pos, double beat_d
if (clips.size() == 0)
return;

Clip* current_clip = event_state.current_clip_idx ? clips[*event_state.current_clip_idx]
: nullptr;
Clip* current_clip =
event_state.current_clip_idx ? clips[*event_state.current_clip_idx] : nullptr;
Clip* next_clip = event_state.next_clip_idx && event_state.next_clip_idx < clips.size()
? clips[*event_state.next_clip_idx]
: nullptr;
Expand Down Expand Up @@ -354,8 +360,7 @@ void Track::process_event(uint32_t buffer_offset, double time_pos, double beat_d
case ClipType::Audio: {
double sample_pos =
beat_to_samples(relative_start_time, sample_rate, beat_duration);
uint64_t sample_offset =
(uint64_t)(sample_pos + next_clip->start_offset);
uint64_t sample_offset = (uint64_t)(sample_pos + next_clip->start_offset);
audio_event_buffer.push_back({
.type = EventType::PlaySample,
.buffer_offset = buffer_offset,
Expand Down Expand Up @@ -498,7 +503,8 @@ void Track::process(AudioBuffer<float>& output_buffer, double sample_rate, bool
switch (queue.id) {
case TrackParameter_Volume:
parameter_state.volume = (float)last_value;
Log::debug("Volume changed: {}", parameter_state.volume);
Log::debug("Volume changed: {} {}", parameter_state.volume,
math::linear_to_db(parameter_state.volume));
break;
case TrackParameter_Pan:
parameter_state.pan = (float)last_value;
Expand Down
10 changes: 8 additions & 2 deletions src/engine/track.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,25 +178,31 @@ struct Track {
* @param name Name of the clip.
* @param min_time Start time in beats.
* @param max_time End time in beats.
* @param start_offset Clip content start offset in sample units.
* @param clip_info Audio clip data.
* @param beat_duration Duration of the beat in seconds.
* @param active Activate clip.
* @return A pointer to the new clip.
*/
Clip* add_audio_clip(const std::string& name, double min_time, double max_time,
const AudioClip& clip_info, double beat_duration);
double start_offset, const AudioClip& clip_info, double beat_duration,
bool active = true);

/**
* @brief Add midi clip into the track.
*
* @param name Name of the clip.
* @param min_time Start time in beats.
* @param max_time End time in beats.
* @param start_offset Clip content start offset in beat units.
* @param clip_info Audio clip data.
* @param beat_duration Duration of the beat in seconds.
* @param active Activate clip.
* @return A pointer to the new clip.
*/
Clip* add_midi_clip(const std::string& name, double min_time, double max_time,
const MidiClip& clip_info, double beat_duration);
double start_offset, const MidiClip& clip_info, double beat_duration,
bool active = true);

/**
* @brief Create a new clip from existing clip.
Expand Down
25 changes: 24 additions & 1 deletion src/ui/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,31 @@ void ClipResizeCmd::undo() {
clip_id = clip->id;
}

void ClipDeleteRegionCmd::execute() {
void ClipDeleteCmd::execute() {
Track* track = g_engine.tracks[track_id];
Clip* clip = track->clips[clip_id];
g_engine.delete_clip(track, clip);
}

void ClipDeleteCmd::undo() {
double beat_duration = g_engine.get_beat_duration();
Track* track = g_engine.tracks[track_id];
g_engine.edit_lock();
if (clip_state.is_audio()) {
clip_state.audio.asset->add_ref();
track->add_audio_clip(clip_state.name, clip_state.min_time, clip_state.max_time,
clip_state.start_offset, clip_state.audio, beat_duration,
clip_state.is_active());
} else {
clip_state.midi.asset->add_ref();
track->add_midi_clip(clip_state.name, clip_state.min_time, clip_state.max_time,
clip_state.start_offset, clip_state.midi, beat_duration,
clip_state.is_active());
}
g_engine.edit_unlock();
}

void ClipDeleteRegionCmd::execute() {
}

void ClipDeleteRegionCmd::undo() {
Expand Down
29 changes: 29 additions & 0 deletions src/ui/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,35 @@ struct ClipDuplicateCmd {
void undo();
};

struct ClipDeleteCmd {
uint32_t track_id;
uint32_t clip_id;
Clip clip_state;
double last_beat_duration;

ClipDeleteCmd(uint32_t track_id, uint32_t clip_id, const Clip& clip,
double last_beat_duration) :
track_id(track_id),
clip_id(clip_id),
clip_state(clip),
last_beat_duration(last_beat_duration) {}

ClipDeleteCmd(ClipDeleteCmd&& other) :
track_id(std::exchange(other.track_id, {})),
clip_id(std::exchange(other.clip_id, {})),
clip_state(std::move(other.clip_state)) {}

ClipDeleteCmd& operator=(ClipDeleteCmd&& other) {
track_id = std::exchange(other.track_id, {});
clip_id = std::exchange(other.clip_id, {});
clip_state = std::move(other.clip_state);
return *this;
}

void execute();
void undo();
};

struct ClipDeleteRegionCmd {
uint32_t first_track_id;
uint32_t last_track_id;
Expand Down
5 changes: 4 additions & 1 deletion src/ui/command_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ void CommandManager::redo() {
signal_all_update_listeners();
}

void CommandManager::clear_history() {
void CommandManager::reset() {
for (uint32_t i = 0; i < size; i++) {
items[i].unset();
}
pos = 0;
size = 0;
}
Expand Down
11 changes: 8 additions & 3 deletions src/ui/command_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ struct EmptyCmd {

struct HistoryItem {
std::string name;
std::variant<EmptyCmd, ClipMoveCmd, ClipShiftCmd, ClipResizeCmd> data;
std::variant<EmptyCmd, ClipMoveCmd, ClipShiftCmd, ClipResizeCmd, ClipDeleteCmd> data;

template <typename T>
template <CommandType T>
void set(const std::string& new_name, T&& new_data) {
name = new_name;
data = std::move(new_data);
}

void unset() {
name.clear();
data = EmptyCmd {};
}
};

struct CommandManager {
Expand All @@ -42,7 +47,7 @@ struct CommandManager {
void init(uint32_t max_items);
void undo();
void redo();
void clear_history();
void reset();
void signal_all_update_listeners();

template <CommandType T>
Expand Down
2 changes: 1 addition & 1 deletion src/ui/history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ void render_history_window() {
}

if (ImGui::Button("Clear All")) {
g_cmd_manager.clear_history();
g_cmd_manager.reset();
}

ImVec2 space = ImGui::GetContentRegionAvail();
Expand Down
8 changes: 6 additions & 2 deletions src/ui/timeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ void GuiTimeline::track_context_menu(Track& track, int track_id) {
ImGui::MenuItem("Duplicate");

if (ImGui::MenuItem("Delete")) {

g_engine.edit_lock();
g_engine.delete_track((uint32_t)track_id);
g_engine.edit_unlock();
Expand Down Expand Up @@ -585,7 +586,10 @@ void GuiTimeline::clip_context_menu() {
}

if (ImGui::MenuItem("Delete")) {
g_engine.delete_clip(context_menu_track, context_menu_clip);
double beat_duration = g_engine.get_beat_duration();
g_cmd_manager.execute("Delete Clip", ClipDeleteCmd(context_menu_track_id.value(),
context_menu_clip->id,
*context_menu_clip, beat_duration));
recalculate_song_length();
force_redraw = true;
}
Expand Down Expand Up @@ -1020,7 +1024,6 @@ void GuiTimeline::render_track_lanes() {
ClipType type = edited_clip->type;
double min_time = edited_clip->min_time;
double max_time = edited_clip->max_time;
double sample_offset = (double)edited_clip->audio.sample_offset;
double relative_pos = mouse_at_gridline - initial_time_pos;
double content_offset = edited_clip->start_offset;

Expand Down Expand Up @@ -1255,6 +1258,7 @@ void GuiTimeline::render_track_lanes() {
break;
case TimelineEditAction::ShowClipContextMenu:
ImGui::OpenPopup("clip_context_menu");
context_menu_track_id = edited_track_id;
context_menu_track = edited_track;
context_menu_clip = edited_clip;
tmp_color = edited_clip->color;
Expand Down
16 changes: 2 additions & 14 deletions src/ui/timeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,11 @@ struct GuiTimeline : public TimelineBase {
ImVec2 area_size;
ImVec2 old_timeline_size;
ImVec2 last_mouse_pos;
//float timeline_width = 0.0f;
//float separator_pos = 150.0f;

/*double inv_ppq = 0.0;
double playhead = 0.0;
double song_length = 10000.0;
double last_hscroll = 0.0;
double min_hscroll = 0.0;
double max_hscroll = 0.074081648971430242;*/

double initial_time_pos = 0.0;
float vscroll = 0.0f;
float last_vscroll = 0.0f;
float scroll_delta_y = 0.0f;
//float grid_scale = 4.0f;

Vector<SelectionRange> selection_ranges;
TargetSelectionRange target_sel_range;
Expand All @@ -73,10 +64,6 @@ struct GuiTimeline : public TimelineBase {
bool zooming = false;
bool selecting_range = false;
bool range_selected = false;
/*bool zooming_on_ruler = false;
bool grabbing_scroll = false;
bool resizing_lhs_scroll_grab = false;
bool resizing_rhs_scroll_grab = false;*/

Track* edited_track {};
Clip* edited_clip {};
Expand All @@ -91,6 +78,7 @@ struct GuiTimeline : public TimelineBase {
float hovered_track_height = 60.0f;

// Context menu stuff...
std::optional<uint32_t> context_menu_track_id {};
Track* context_menu_track {};
Clip* context_menu_clip {};
ImColor tmp_color;
Expand Down

0 comments on commit afcfe6e

Please sign in to comment.