mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
editor: Implement drag and drop where it makes sense
This commit is contained in:
parent
a13e406e99
commit
e47b7c2006
31 changed files with 888 additions and 87 deletions
|
|
@ -119,3 +119,17 @@
|
|||
padding: 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
color: color-mix(in srgb, var(--window-fg-color) 40%, transparent);
|
||||
}
|
||||
|
||||
.drag-handle:backdrop {
|
||||
color: color-mix(in srgb, var(--window-fg-color) 40%, transparent);
|
||||
}
|
||||
|
||||
dragwidget {
|
||||
background-color: var(--card-bg-color);
|
||||
color: var(--card-fg-color);
|
||||
border: 1px solid rgba(0, 0, 6, 0.07);
|
||||
}
|
||||
23
data/ui/editor/album/recording_row.blp
Normal file
23
data/ui/editor/album/recording_row.blp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $MusicusAlbumEditorRecordingRow: Adw.ActionRow {
|
||||
[prefix]
|
||||
Gtk.Image {
|
||||
icon-name: "list-drag-handle-symbolic";
|
||||
|
||||
styles [
|
||||
"drag-handle",
|
||||
]
|
||||
}
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "user-trash-symbolic";
|
||||
valign: center;
|
||||
clicked => $remove() swapped;
|
||||
|
||||
styles [
|
||||
"flat",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,15 @@ using Gtk 4.0;
|
|||
using Adw 1;
|
||||
|
||||
template $MusicusRecordingEditorEnsembleRow: Adw.ActionRow {
|
||||
[prefix]
|
||||
Gtk.Image {
|
||||
icon-name: "list-drag-handle-symbolic";
|
||||
|
||||
styles [
|
||||
"drag-handle",
|
||||
]
|
||||
}
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "user-trash-symbolic";
|
||||
valign: center;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@ using Gtk 4.0;
|
|||
using Adw 1;
|
||||
|
||||
template $MusicusRecordingEditorPerformerRow: Adw.ActionRow {
|
||||
[prefix]
|
||||
Gtk.Image {
|
||||
icon-name: "list-drag-handle-symbolic";
|
||||
|
||||
styles [
|
||||
"drag-handle",
|
||||
]
|
||||
}
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "user-trash-symbolic";
|
||||
valign: center;
|
||||
|
|
|
|||
|
|
@ -9,10 +9,18 @@ template $MusicusTracksEditorTrackRow: Adw.ActionRow {
|
|||
[prefix]
|
||||
Gtk.Box select_parts_box {
|
||||
Gtk.Image {
|
||||
icon-name: "document-edit-symbolic";
|
||||
icon-name: "list-drag-handle-symbolic";
|
||||
|
||||
styles [
|
||||
"drag-handle",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Gtk.Image edit_image {
|
||||
icon-name: "document-edit-symbolic";
|
||||
}
|
||||
|
||||
Gtk.Button reset_button {
|
||||
icon-name: "edit-clear-symbolic";
|
||||
tooltip-text: _("Clear selected work parts");
|
||||
|
|
@ -21,7 +29,7 @@ template $MusicusTracksEditorTrackRow: Adw.ActionRow {
|
|||
clicked => $reset() swapped;
|
||||
|
||||
styles [
|
||||
"flat"
|
||||
"flat",
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +40,7 @@ template $MusicusTracksEditorTrackRow: Adw.ActionRow {
|
|||
clicked => $remove() swapped;
|
||||
|
||||
styles [
|
||||
"flat"
|
||||
"flat",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,22 @@ using Gtk 4.0;
|
|||
using Adw 1;
|
||||
|
||||
template $MusicusWorkEditorComposerRow: Adw.ActionRow {
|
||||
[prefix]
|
||||
Gtk.Image {
|
||||
icon-name: "list-drag-handle-symbolic";
|
||||
|
||||
styles [
|
||||
"drag-handle",
|
||||
]
|
||||
}
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "user-trash-symbolic";
|
||||
valign: center;
|
||||
clicked => $remove() swapped;
|
||||
|
||||
styles [
|
||||
"flat"
|
||||
"flat",
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +26,7 @@ template $MusicusWorkEditorComposerRow: Adw.ActionRow {
|
|||
clicked => $open_role_popover() swapped;
|
||||
|
||||
styles [
|
||||
"flat"
|
||||
"flat",
|
||||
]
|
||||
|
||||
Gtk.Box role_box {
|
||||
|
|
|
|||
23
data/ui/editor/work/instrument_row.blp
Normal file
23
data/ui/editor/work/instrument_row.blp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $MusicusWorkEditorInstrumentRow: Adw.ActionRow {
|
||||
[prefix]
|
||||
Gtk.Image {
|
||||
icon-name: "list-drag-handle-symbolic";
|
||||
|
||||
styles [
|
||||
"drag-handle",
|
||||
]
|
||||
}
|
||||
|
||||
Gtk.Button {
|
||||
icon-name: "user-trash-symbolic";
|
||||
valign: center;
|
||||
clicked => $remove() swapped;
|
||||
|
||||
styles [
|
||||
"flat",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,15 @@ template $MusicusWorkEditorPartRow: Adw.ActionRow {
|
|||
activatable: true;
|
||||
activated => $edit() swapped;
|
||||
|
||||
[prefix]
|
||||
Gtk.Image {
|
||||
icon-name: "list-drag-handle-symbolic";
|
||||
|
||||
styles [
|
||||
"drag-handle",
|
||||
]
|
||||
}
|
||||
|
||||
Gtk.Image {
|
||||
icon-name: "document-edit-symbolic";
|
||||
}
|
||||
|
|
@ -15,7 +24,7 @@ template $MusicusWorkEditorPartRow: Adw.ActionRow {
|
|||
clicked => $remove() swapped;
|
||||
|
||||
styles [
|
||||
"flat"
|
||||
"flat",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
mod recording_row;
|
||||
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::glib::{self, clone, subclass::Signal, Properties};
|
||||
use once_cell::sync::Lazy;
|
||||
use recording_row::RecordingRow;
|
||||
|
||||
use crate::{
|
||||
db::models::{Album, Recording},
|
||||
|
|
@ -25,7 +28,7 @@ mod imp {
|
|||
pub library: OnceCell<Library>,
|
||||
|
||||
pub album_id: OnceCell<String>,
|
||||
pub recordings: RefCell<Vec<Recording>>,
|
||||
pub recording_rows: RefCell<Vec<RecordingRow>>,
|
||||
|
||||
pub recordings_popover: OnceCell<RecordingSelectorPopover>,
|
||||
|
||||
|
|
@ -143,40 +146,36 @@ impl AlbumEditor {
|
|||
}
|
||||
|
||||
fn add_recording(&self, recording: Recording) {
|
||||
let row = adw::ActionRow::builder()
|
||||
.title(recording.work.to_string())
|
||||
.subtitle(recording.performers_string())
|
||||
.build();
|
||||
let row = RecordingRow::new(recording);
|
||||
|
||||
let remove_button = gtk::Button::builder()
|
||||
.icon_name("user-trash-symbolic")
|
||||
.valign(gtk::Align::Center)
|
||||
.css_classes(["flat"])
|
||||
.build();
|
||||
|
||||
remove_button.connect_clicked(clone!(
|
||||
row.connect_move(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
#[weak]
|
||||
row,
|
||||
#[strong]
|
||||
recording,
|
||||
move |_| {
|
||||
this.imp().recordings_list.remove(&row);
|
||||
this.imp()
|
||||
.recordings
|
||||
.borrow_mut()
|
||||
.retain(|r| *r != recording);
|
||||
move |target, source| {
|
||||
let mut recording_rows = this.imp().recording_rows.borrow_mut();
|
||||
if let Some(index) = recording_rows.iter().position(|p| p == target) {
|
||||
this.imp().recordings_list.remove(&source);
|
||||
recording_rows.retain(|p| p != &source);
|
||||
this.imp().recordings_list.insert(&source, index as i32);
|
||||
recording_rows.insert(index, source);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
row.add_suffix(&remove_button);
|
||||
row.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |row| {
|
||||
this.imp().recordings_list.remove(row);
|
||||
this.imp().recording_rows.borrow_mut().retain(|p| p != row);
|
||||
}
|
||||
));
|
||||
|
||||
self.imp()
|
||||
.recordings_list
|
||||
.insert(&row, self.imp().recordings.borrow().len() as i32);
|
||||
.insert(&row, self.imp().recording_rows.borrow().len() as i32);
|
||||
|
||||
self.imp().recordings.borrow_mut().push(recording);
|
||||
self.imp().recording_rows.borrow_mut().push(row);
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
|
|
@ -184,7 +183,13 @@ impl AlbumEditor {
|
|||
let library = self.imp().library.get().unwrap();
|
||||
|
||||
let name = self.imp().name_editor.translation();
|
||||
let recordings = self.imp().recordings.borrow().clone();
|
||||
let recordings = self
|
||||
.imp()
|
||||
.recording_rows
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|r| r.recording())
|
||||
.collect::<Vec<Recording>>();
|
||||
|
||||
if let Some(album_id) = self.imp().album_id.get() {
|
||||
library.update_album(album_id, name, recordings).unwrap();
|
||||
|
|
|
|||
140
src/editor/album/recording_row.rs
Normal file
140
src/editor/album/recording_row.rs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
use std::cell::OnceCell;
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{db::models::Recording, util::drag_widget::DragWidget};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
#[template(file = "data/ui/editor/album/recording_row.blp")]
|
||||
pub struct RecordingRow {
|
||||
pub recording: OnceCell<Recording>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for RecordingRow {
|
||||
const NAME: &'static str = "MusicusAlbumEditorRecordingRow";
|
||||
type Type = super::RecordingRow;
|
||||
type ParentType = adw::ActionRow;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for RecordingRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("remove").build(),
|
||||
Signal::builder("move")
|
||||
.param_types([super::RecordingRow::static_type()])
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let drag_source = gtk::DragSource::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.content(&gdk::ContentProvider::for_value(&self.obj().to_value()))
|
||||
.build();
|
||||
|
||||
drag_source.connect_drag_begin(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
move |_, drag| {
|
||||
let icon = gtk::DragIcon::for_drag(drag);
|
||||
icon.set_child(Some(&DragWidget::new(&obj)));
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drag_source);
|
||||
|
||||
let drop_target = gtk::DropTarget::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.build();
|
||||
drop_target.set_types(&[Self::Type::static_type()]);
|
||||
|
||||
drop_target.connect_drop(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
#[upgrade_or]
|
||||
false,
|
||||
move |_, value, _, _| {
|
||||
if let Ok(row) = value.get::<Self::Type>() {
|
||||
obj.emit_by_name::<()>("move", &[&row]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drop_target);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for RecordingRow {}
|
||||
impl ListBoxRowImpl for RecordingRow {}
|
||||
impl PreferencesRowImpl for RecordingRow {}
|
||||
impl ActionRowImpl for RecordingRow {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct RecordingRow(ObjectSubclass<imp::RecordingRow>)
|
||||
@extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl RecordingRow {
|
||||
pub fn new(recording: Recording) -> Self {
|
||||
let obj: Self = glib::Object::new();
|
||||
obj.set_title(&recording.work.to_string());
|
||||
obj.set_subtitle(&recording.performers_string());
|
||||
obj.imp().recording.set(recording).unwrap();
|
||||
obj
|
||||
}
|
||||
|
||||
pub fn connect_move<F: Fn(&Self, Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("move", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let source = values[1].get::<Self>().unwrap();
|
||||
f(&obj, source);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("remove", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recording(&self) -> Recording {
|
||||
self.imp().recording.get().unwrap().clone()
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn remove(&self) {
|
||||
self.emit_by_name::<()>("remove", &[]);
|
||||
}
|
||||
}
|
||||
|
|
@ -266,6 +266,20 @@ impl RecordingEditor {
|
|||
fn add_performer_row(&self, performer: Performer) {
|
||||
let row = RecordingEditorPerformerRow::new(&self.navigation(), &self.library(), performer);
|
||||
|
||||
row.connect_move(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |target, source| {
|
||||
let mut performer_rows = this.imp().performer_rows.borrow_mut();
|
||||
if let Some(index) = performer_rows.iter().position(|p| p == target) {
|
||||
this.imp().performer_list.remove(&source);
|
||||
performer_rows.retain(|p| p != &source);
|
||||
this.imp().performer_list.insert(&source, index as i32);
|
||||
performer_rows.insert(index, source);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
row.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
|
|
@ -298,6 +312,20 @@ impl RecordingEditor {
|
|||
ensemble_performer,
|
||||
);
|
||||
|
||||
row.connect_move(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |target, source| {
|
||||
let mut ensemble_rows = this.imp().ensemble_rows.borrow_mut();
|
||||
if let Some(index) = ensemble_rows.iter().position(|p| p == target) {
|
||||
this.imp().ensemble_list.remove(&source);
|
||||
ensemble_rows.retain(|p| p != &source);
|
||||
this.imp().ensemble_list.insert(&source, index as i32);
|
||||
ensemble_rows.insert(index, source);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
row.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::glib::{self, clone, subclass::Signal, Properties};
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal, Properties},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
db::models::EnsemblePerformer, editor::role::RoleEditor, library::Library,
|
||||
selector::role::RoleSelectorPopover,
|
||||
selector::role::RoleSelectorPopover, util::drag_widget::DragWidget,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
@ -50,8 +53,14 @@ mod imp {
|
|||
#[glib::derived_properties]
|
||||
impl ObjectImpl for RecordingEditorEnsembleRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> =
|
||||
Lazy::new(|| vec![Signal::builder("remove").build()]);
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("remove").build(),
|
||||
Signal::builder("move")
|
||||
.param_types([super::RecordingEditorEnsembleRow::static_type()])
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
|
@ -59,6 +68,44 @@ mod imp {
|
|||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let drag_source = gtk::DragSource::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.content(&gdk::ContentProvider::for_value(&self.obj().to_value()))
|
||||
.build();
|
||||
|
||||
drag_source.connect_drag_begin(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
move |_, drag| {
|
||||
let icon = gtk::DragIcon::for_drag(drag);
|
||||
icon.set_child(Some(&DragWidget::new(&obj)));
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drag_source);
|
||||
|
||||
let drop_target = gtk::DropTarget::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.build();
|
||||
drop_target.set_types(&[Self::Type::static_type()]);
|
||||
|
||||
drop_target.connect_drop(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
#[upgrade_or]
|
||||
false,
|
||||
move |_, value, _, _| {
|
||||
if let Ok(row) = value.get::<Self::Type>() {
|
||||
obj.emit_by_name::<()>("move", &[&row]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drop_target);
|
||||
|
||||
let role_popover = RoleSelectorPopover::new(self.library.get().unwrap());
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
|
|
@ -118,6 +165,15 @@ impl RecordingEditorEnsembleRow {
|
|||
obj
|
||||
}
|
||||
|
||||
pub fn connect_move<F: Fn(&Self, Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("move", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let source = values[1].get::<Self>().unwrap();
|
||||
f(&obj, source);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("remove", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::glib::{self, clone, subclass::Signal, Properties};
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal, Properties},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
db::models::Performer, editor::role::RoleEditor, library::Library,
|
||||
selector::performer_role::PerformerRoleSelectorPopover,
|
||||
selector::performer_role::PerformerRoleSelectorPopover, util::drag_widget::DragWidget,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
@ -51,8 +54,14 @@ mod imp {
|
|||
#[glib::derived_properties]
|
||||
impl ObjectImpl for RecordingEditorPerformerRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> =
|
||||
Lazy::new(|| vec![Signal::builder("remove").build()]);
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("remove").build(),
|
||||
Signal::builder("move")
|
||||
.param_types([super::RecordingEditorPerformerRow::static_type()])
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
|
@ -60,6 +69,44 @@ mod imp {
|
|||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let drag_source = gtk::DragSource::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.content(&gdk::ContentProvider::for_value(&self.obj().to_value()))
|
||||
.build();
|
||||
|
||||
drag_source.connect_drag_begin(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
move |_, drag| {
|
||||
let icon = gtk::DragIcon::for_drag(drag);
|
||||
icon.set_child(Some(&DragWidget::new(&obj)));
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drag_source);
|
||||
|
||||
let drop_target = gtk::DropTarget::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.build();
|
||||
drop_target.set_types(&[Self::Type::static_type()]);
|
||||
|
||||
drop_target.connect_drop(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
#[upgrade_or]
|
||||
false,
|
||||
move |_, value, _, _| {
|
||||
if let Ok(row) = value.get::<Self::Type>() {
|
||||
obj.emit_by_name::<()>("move", &[&row]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drop_target);
|
||||
|
||||
let role_popover = PerformerRoleSelectorPopover::new(self.library.get().unwrap());
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
|
|
@ -142,6 +189,15 @@ impl RecordingEditorPerformerRow {
|
|||
obj
|
||||
}
|
||||
|
||||
pub fn connect_move<F: Fn(&Self, Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("move", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let source = values[1].get::<Self>().unwrap();
|
||||
f(&obj, source);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("remove", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
|
|
|
|||
|
|
@ -268,6 +268,20 @@ impl TracksEditor {
|
|||
let track_row =
|
||||
TracksEditorTrackRow::new(&self.navigation(), &self.library(), recording, track_data);
|
||||
|
||||
track_row.connect_move(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |target, source| {
|
||||
let mut track_rows = this.imp().track_rows.borrow_mut();
|
||||
if let Some(index) = track_rows.iter().position(|p| p == target) {
|
||||
this.imp().track_list.remove(&source);
|
||||
track_rows.retain(|p| p != &source);
|
||||
this.imp().track_list.insert(&source, index as i32);
|
||||
track_rows.insert(index, source);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
track_row.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use gtk::{
|
|||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{activatable_row::ActivatableRow, db::models::Work};
|
||||
use crate::{db::models::Work, util::activatable_row::ActivatableRow};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -6,13 +6,17 @@ use std::{
|
|||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use formatx::formatx;
|
||||
use gettextrs::gettext;
|
||||
use gtk::glib::{self, clone, subclass::Signal, Properties};
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal, Properties},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::parts_popover::TracksEditorPartsPopover;
|
||||
use crate::{
|
||||
db::models::{Recording, Track, Work},
|
||||
library::Library,
|
||||
util::drag_widget::DragWidget,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
@ -35,6 +39,8 @@ mod imp {
|
|||
#[template_child]
|
||||
pub select_parts_box: TemplateChild<gtk::Box>,
|
||||
#[template_child]
|
||||
pub edit_image: TemplateChild<gtk::Image>,
|
||||
#[template_child]
|
||||
pub reset_button: TemplateChild<gtk::Button>,
|
||||
}
|
||||
|
||||
|
|
@ -57,11 +63,59 @@ mod imp {
|
|||
#[glib::derived_properties]
|
||||
impl ObjectImpl for TracksEditorTrackRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> =
|
||||
Lazy::new(|| vec![Signal::builder("remove").build()]);
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("remove").build(),
|
||||
Signal::builder("move")
|
||||
.param_types([super::TracksEditorTrackRow::static_type()])
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let drag_source = gtk::DragSource::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.content(&gdk::ContentProvider::for_value(&self.obj().to_value()))
|
||||
.build();
|
||||
|
||||
drag_source.connect_drag_begin(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
move |_, drag| {
|
||||
let icon = gtk::DragIcon::for_drag(drag);
|
||||
icon.set_child(Some(&DragWidget::new(&obj)));
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drag_source);
|
||||
|
||||
let drop_target = gtk::DropTarget::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.build();
|
||||
drop_target.set_types(&[Self::Type::static_type()]);
|
||||
|
||||
drop_target.connect_drop(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
#[upgrade_or]
|
||||
false,
|
||||
move |_, value, _, _| {
|
||||
if let Ok(row) = value.get::<Self::Type>() {
|
||||
obj.emit_by_name::<()>("move", &[&row]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drop_target);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for TracksEditorTrackRow {}
|
||||
|
|
@ -89,6 +143,9 @@ impl TracksEditorTrackRow {
|
|||
.build();
|
||||
|
||||
obj.set_activatable(!recording.work.parts.is_empty());
|
||||
obj.imp()
|
||||
.edit_image
|
||||
.set_visible(!recording.work.parts.is_empty());
|
||||
|
||||
obj.set_subtitle(&match &track_data.location {
|
||||
TrackLocation::Undefined => String::new(),
|
||||
|
|
@ -127,6 +184,15 @@ impl TracksEditorTrackRow {
|
|||
obj
|
||||
}
|
||||
|
||||
pub fn connect_move<F: Fn(&Self, Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("move", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let source = values[1].get::<Self>().unwrap();
|
||||
f(&obj, source);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("remove", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
mod composer_row;
|
||||
mod instrument_row;
|
||||
mod part_row;
|
||||
|
||||
use std::cell::{Cell, OnceCell, RefCell};
|
||||
|
|
@ -19,6 +20,7 @@ use crate::{
|
|||
library::Library,
|
||||
selector::{instrument::InstrumentSelectorPopover, person::PersonSelectorPopover},
|
||||
};
|
||||
use instrument_row::InstrumentRow;
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
|
@ -41,8 +43,7 @@ mod imp {
|
|||
// handle all state related to the composer.
|
||||
pub composer_rows: RefCell<Vec<WorkEditorComposerRow>>,
|
||||
pub part_rows: RefCell<Vec<WorkEditorPartRow>>,
|
||||
|
||||
pub instruments: RefCell<Vec<Instrument>>,
|
||||
pub instrument_rows: RefCell<Vec<InstrumentRow>>,
|
||||
|
||||
pub persons_popover: OnceCell<PersonSelectorPopover>,
|
||||
pub instruments_popover: OnceCell<InstrumentSelectorPopover>,
|
||||
|
|
@ -240,6 +241,20 @@ impl WorkEditor {
|
|||
fn add_part_row(&self, part: Work) {
|
||||
let row = WorkEditorPartRow::new(&self.navigation(), &self.library(), part);
|
||||
|
||||
row.connect_move(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |target, source| {
|
||||
let mut part_rows = this.imp().part_rows.borrow_mut();
|
||||
if let Some(index) = part_rows.iter().position(|p| p == target) {
|
||||
this.imp().part_list.remove(&source);
|
||||
part_rows.retain(|p| p != &source);
|
||||
this.imp().part_list.insert(&source, index as i32);
|
||||
part_rows.insert(index, source);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
row.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
|
|
@ -259,6 +274,20 @@ impl WorkEditor {
|
|||
fn add_composer_row(&self, composer: Composer) {
|
||||
let row = WorkEditorComposerRow::new(&self.navigation(), &self.library(), composer);
|
||||
|
||||
row.connect_move(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |target, source| {
|
||||
let mut composer_rows = this.imp().composer_rows.borrow_mut();
|
||||
if let Some(index) = composer_rows.iter().position(|p| p == target) {
|
||||
this.imp().composer_list.remove(&source);
|
||||
composer_rows.retain(|p| p != &source);
|
||||
this.imp().composer_list.insert(&source, index as i32);
|
||||
composer_rows.insert(index, source);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
row.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
|
|
@ -276,39 +305,36 @@ impl WorkEditor {
|
|||
}
|
||||
|
||||
fn add_instrument_row(&self, instrument: Instrument) {
|
||||
let row = adw::ActionRow::builder()
|
||||
.title(instrument.to_string())
|
||||
.build();
|
||||
let row = InstrumentRow::new(instrument);
|
||||
|
||||
let remove_button = gtk::Button::builder()
|
||||
.icon_name("user-trash-symbolic")
|
||||
.valign(gtk::Align::Center)
|
||||
.css_classes(["flat"])
|
||||
.build();
|
||||
|
||||
remove_button.connect_clicked(clone!(
|
||||
row.connect_move(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
#[weak]
|
||||
row,
|
||||
#[strong]
|
||||
instrument,
|
||||
move |_| {
|
||||
this.imp().instrument_list.remove(&row);
|
||||
this.imp()
|
||||
.instruments
|
||||
.borrow_mut()
|
||||
.retain(|i| *i != instrument);
|
||||
move |target, source| {
|
||||
let mut instrument_rows = this.imp().instrument_rows.borrow_mut();
|
||||
if let Some(index) = instrument_rows.iter().position(|p| p == target) {
|
||||
this.imp().instrument_list.remove(&source);
|
||||
instrument_rows.retain(|p| p != &source);
|
||||
this.imp().instrument_list.insert(&source, index as i32);
|
||||
instrument_rows.insert(index, source);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
row.add_suffix(&remove_button);
|
||||
row.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |row| {
|
||||
this.imp().instrument_list.remove(row);
|
||||
this.imp().instrument_rows.borrow_mut().retain(|p| p != row);
|
||||
}
|
||||
));
|
||||
|
||||
self.imp()
|
||||
.instrument_list
|
||||
.insert(&row, self.imp().instruments.borrow().len() as i32);
|
||||
.insert(&row, self.imp().instrument_rows.borrow().len() as i32);
|
||||
|
||||
self.imp().instruments.borrow_mut().push(instrument);
|
||||
self.imp().instrument_rows.borrow_mut().push(row);
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
|
|
@ -332,7 +358,14 @@ impl WorkEditor {
|
|||
.iter()
|
||||
.map(|c| c.composer())
|
||||
.collect::<Vec<Composer>>();
|
||||
let instruments = self.imp().instruments.borrow().clone();
|
||||
|
||||
let instruments = self
|
||||
.imp()
|
||||
.instrument_rows
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|r| r.instrument())
|
||||
.collect::<Vec<Instrument>>();
|
||||
|
||||
if self.imp().is_part_editor.get() {
|
||||
let work_id = self
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::glib::{self, clone, subclass::Signal, Properties};
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal, Properties},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
db::models::Composer, editor::role::RoleEditor, library::Library,
|
||||
selector::role::RoleSelectorPopover,
|
||||
selector::role::RoleSelectorPopover, util::drag_widget::DragWidget,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
@ -50,8 +53,14 @@ mod imp {
|
|||
#[glib::derived_properties]
|
||||
impl ObjectImpl for WorkEditorComposerRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> =
|
||||
Lazy::new(|| vec![Signal::builder("remove").build()]);
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("remove").build(),
|
||||
Signal::builder("move")
|
||||
.param_types([super::WorkEditorComposerRow::static_type()])
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
|
@ -59,6 +68,44 @@ mod imp {
|
|||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let drag_source = gtk::DragSource::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.content(&gdk::ContentProvider::for_value(&self.obj().to_value()))
|
||||
.build();
|
||||
|
||||
drag_source.connect_drag_begin(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
move |_, drag| {
|
||||
let icon = gtk::DragIcon::for_drag(drag);
|
||||
icon.set_child(Some(&DragWidget::new(&obj)));
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drag_source);
|
||||
|
||||
let drop_target = gtk::DropTarget::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.build();
|
||||
drop_target.set_types(&[Self::Type::static_type()]);
|
||||
|
||||
drop_target.connect_drop(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
#[upgrade_or]
|
||||
false,
|
||||
move |_, value, _, _| {
|
||||
if let Ok(row) = value.get::<Self::Type>() {
|
||||
obj.emit_by_name::<()>("move", &[&row]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drop_target);
|
||||
|
||||
let role_popover = RoleSelectorPopover::new(self.library.get().unwrap());
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
|
|
@ -114,6 +161,15 @@ impl WorkEditorComposerRow {
|
|||
obj
|
||||
}
|
||||
|
||||
pub fn connect_move<F: Fn(&Self, Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("move", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let source = values[1].get::<Self>().unwrap();
|
||||
f(&obj, source);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("remove", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
|
|
|
|||
139
src/editor/work/instrument_row.rs
Normal file
139
src/editor/work/instrument_row.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
use std::cell::OnceCell;
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{db::models::Instrument, util::drag_widget::DragWidget};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
#[template(file = "data/ui/editor/work/instrument_row.blp")]
|
||||
pub struct InstrumentRow {
|
||||
pub instrument: OnceCell<Instrument>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for InstrumentRow {
|
||||
const NAME: &'static str = "MusicusWorkEditorInstrumentRow";
|
||||
type Type = super::InstrumentRow;
|
||||
type ParentType = adw::ActionRow;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for InstrumentRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("remove").build(),
|
||||
Signal::builder("move")
|
||||
.param_types([super::InstrumentRow::static_type()])
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let drag_source = gtk::DragSource::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.content(&gdk::ContentProvider::for_value(&self.obj().to_value()))
|
||||
.build();
|
||||
|
||||
drag_source.connect_drag_begin(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
move |_, drag| {
|
||||
let icon = gtk::DragIcon::for_drag(drag);
|
||||
icon.set_child(Some(&DragWidget::new(&obj)));
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drag_source);
|
||||
|
||||
let drop_target = gtk::DropTarget::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.build();
|
||||
drop_target.set_types(&[Self::Type::static_type()]);
|
||||
|
||||
drop_target.connect_drop(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
#[upgrade_or]
|
||||
false,
|
||||
move |_, value, _, _| {
|
||||
if let Ok(row) = value.get::<Self::Type>() {
|
||||
obj.emit_by_name::<()>("move", &[&row]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drop_target);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for InstrumentRow {}
|
||||
impl ListBoxRowImpl for InstrumentRow {}
|
||||
impl PreferencesRowImpl for InstrumentRow {}
|
||||
impl ActionRowImpl for InstrumentRow {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct InstrumentRow(ObjectSubclass<imp::InstrumentRow>)
|
||||
@extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl InstrumentRow {
|
||||
pub fn new(instrument: Instrument) -> Self {
|
||||
let obj: Self = glib::Object::new();
|
||||
obj.set_title(&instrument.to_string());
|
||||
obj.imp().instrument.set(instrument).unwrap();
|
||||
obj
|
||||
}
|
||||
|
||||
pub fn connect_move<F: Fn(&Self, Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("move", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let source = values[1].get::<Self>().unwrap();
|
||||
f(&obj, source);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("remove", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn instrument(&self) -> Instrument {
|
||||
self.imp().instrument.get().unwrap().clone()
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn remove(&self) {
|
||||
self.emit_by_name::<()>("remove", &[]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,17 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::glib::{self, clone, subclass::Signal, Properties};
|
||||
use gtk::{
|
||||
gdk,
|
||||
glib::{self, clone, subclass::Signal, Properties},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{db::models::Work, editor::work::WorkEditor, library::Library};
|
||||
use crate::{
|
||||
db::models::Work, editor::work::WorkEditor, library::Library, util::drag_widget::DragWidget,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Properties, Debug, Default, gtk::CompositeTemplate)]
|
||||
|
|
@ -42,11 +46,59 @@ mod imp {
|
|||
#[glib::derived_properties]
|
||||
impl ObjectImpl for WorkEditorPartRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> =
|
||||
Lazy::new(|| vec![Signal::builder("remove").build()]);
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("remove").build(),
|
||||
Signal::builder("move")
|
||||
.param_types([super::WorkEditorPartRow::static_type()])
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let drag_source = gtk::DragSource::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.content(&gdk::ContentProvider::for_value(&self.obj().to_value()))
|
||||
.build();
|
||||
|
||||
drag_source.connect_drag_begin(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
move |_, drag| {
|
||||
let icon = gtk::DragIcon::for_drag(drag);
|
||||
icon.set_child(Some(&DragWidget::new(&obj)));
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drag_source);
|
||||
|
||||
let drop_target = gtk::DropTarget::builder()
|
||||
.actions(gdk::DragAction::MOVE)
|
||||
.build();
|
||||
drop_target.set_types(&[Self::Type::static_type()]);
|
||||
|
||||
drop_target.connect_drop(clone!(
|
||||
#[weak(rename_to = obj)]
|
||||
self.obj(),
|
||||
#[upgrade_or]
|
||||
false,
|
||||
move |_, value, _, _| {
|
||||
if let Ok(row) = value.get::<Self::Type>() {
|
||||
obj.emit_by_name::<()>("move", &[&row]);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.obj().add_controller(drop_target);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for WorkEditorPartRow {}
|
||||
|
|
@ -71,6 +123,15 @@ impl WorkEditorPartRow {
|
|||
obj
|
||||
}
|
||||
|
||||
pub fn connect_move<F: Fn(&Self, Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("move", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
let source = values[1].get::<Self>().unwrap();
|
||||
f(&obj, source);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("remove", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
mod activatable_row;
|
||||
mod album_tile;
|
||||
mod application;
|
||||
mod config;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use gtk::{
|
|||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{activatable_row::ActivatableRow, db::models::Ensemble, library::Library};
|
||||
use crate::{db::models::Ensemble, library::Library, util::activatable_row::ActivatableRow};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use gtk::{
|
|||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{activatable_row::ActivatableRow, db::models::Instrument, library::Library};
|
||||
use crate::{db::models::Instrument, library::Library, util::activatable_row::ActivatableRow};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ use gtk::{
|
|||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
activatable_row::ActivatableRow,
|
||||
db::models::{Instrument, Role},
|
||||
library::Library,
|
||||
util::activatable_row::ActivatableRow,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use gtk::{
|
|||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{activatable_row::ActivatableRow, db::models::Person, library::Library};
|
||||
use crate::{db::models::Person, library::Library, util::activatable_row::ActivatableRow};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ use gtk::{
|
|||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
activatable_row::ActivatableRow,
|
||||
db::models::{Person, Recording, Work},
|
||||
library::Library,
|
||||
util::activatable_row::ActivatableRow,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use gtk::{
|
|||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{activatable_row::ActivatableRow, db::models::Role, library::Library};
|
||||
use crate::{db::models::Role, library::Library, util::activatable_row::ActivatableRow};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ use gtk::{
|
|||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
activatable_row::ActivatableRow,
|
||||
db::models::{Person, Work},
|
||||
library::Library,
|
||||
util::activatable_row::ActivatableRow,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
pub mod activatable_row;
|
||||
pub mod drag_widget;
|
||||
|
||||
use gtk::glib;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
|
|
|
|||
41
src/util/drag_widget.rs
Normal file
41
src/util/drag_widget.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
use adw::{prelude::*, subclass::prelude::*};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DragWidget {}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for DragWidget {
|
||||
const NAME: &'static str = "MusicusDragWidget";
|
||||
type Type = super::DragWidget;
|
||||
type ParentType = adw::Bin;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.set_css_name("dragwidget");
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for DragWidget {}
|
||||
impl WidgetImpl for DragWidget {}
|
||||
impl BinImpl for DragWidget {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A simple helper widget for displaying a drag icon for a widget.
|
||||
pub struct DragWidget(ObjectSubclass<imp::DragWidget>)
|
||||
@extends gtk::Widget, adw::Bin;
|
||||
}
|
||||
|
||||
impl DragWidget {
|
||||
pub fn new<W>(widget: &W) -> Self
|
||||
where
|
||||
W: IsA<gtk::Widget>,
|
||||
{
|
||||
let obj: Self = glib::Object::new();
|
||||
let picture = gtk::Picture::for_paintable(>k::WidgetPaintable::new(Some(widget)));
|
||||
obj.set_child(Some(&picture));
|
||||
obj
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue