mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-23 01:57:29 +00:00
Support shift click and meta click in playlist
This commit is contained in:
parent
b625287e0b
commit
7e7e217075
4 changed files with 67 additions and 26 deletions
|
|
@ -42,6 +42,7 @@ export const UNSET_USER_MESSAGE = "UNSET_USER_MESSAGE";
|
|||
export const SET_PLAYLIST_SCROLL_POSITION = "SET_PLAYLIST_SCROLL_POSITION";
|
||||
export const CLICKED_TRACK = "CLICKED_TRACK";
|
||||
export const CTRL_CLICKED_TRACK = "CTRL_CLICKED_TRACK";
|
||||
export const SHIFT_CLICKED_TRACK = "SHIFT_CLICKED_TRACK";
|
||||
export const SELECT_ALL = "SELECT_ALL";
|
||||
export const SELECT_ZERO = "SELECT_ZERO";
|
||||
export const INVERT_SELECTION = "INVERT_SELECTION";
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import classnames from "classnames";
|
|||
import {
|
||||
CLICKED_TRACK,
|
||||
CTRL_CLICKED_TRACK,
|
||||
SHIFT_CLICKED_TRACK,
|
||||
PLAY_TRACK
|
||||
} from "../../actionTypes";
|
||||
import { getCurrentTrackId } from "../../selectors";
|
||||
|
|
@ -14,7 +15,6 @@ const TrackCell = props => {
|
|||
selected,
|
||||
current,
|
||||
clickTrack,
|
||||
ctrlClickTrack,
|
||||
playTrack,
|
||||
children,
|
||||
onMouseDown
|
||||
|
|
@ -30,7 +30,7 @@ const TrackCell = props => {
|
|||
onMouseDown={onMouseDown}
|
||||
onClick={clickTrack}
|
||||
onDoubleClick={playTrack}
|
||||
onContextMenu={ctrlClickTrack}
|
||||
onContextMenu={clickTrack}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
|
@ -45,14 +45,18 @@ const mapStateToProps = (state, ownProps) => {
|
|||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, ownProps) => ({
|
||||
clickTrack: () => dispatch({ type: CLICKED_TRACK, id: ownProps.id }),
|
||||
ctrlClickTrack: e => {
|
||||
if (e.ctrlKey) {
|
||||
clickTrack: e => {
|
||||
if (e.shiftKey) {
|
||||
e.preventDefault();
|
||||
return dispatch({ type: CTRL_CLICKED_TRACK, id: ownProps.id });
|
||||
return dispatch({ type: SHIFT_CLICKED_TRACK, index: ownProps.index });
|
||||
} else if (e.metaKey) {
|
||||
e.preventDefault();
|
||||
return dispatch({ type: CTRL_CLICKED_TRACK, index: ownProps.index });
|
||||
} else if (e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
return dispatch({ type: CTRL_CLICKED_TRACK, index: ownProps.index });
|
||||
}
|
||||
return null;
|
||||
// TODO: We need to spawn our own context menu
|
||||
return dispatch({ type: CLICKED_TRACK, index: ownProps.index });
|
||||
},
|
||||
playTrack: () => dispatch({ type: PLAY_TRACK, id: ownProps.id })
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class TrackList extends React.Component {
|
|||
|
||||
_renderTracks(format) {
|
||||
return this.props.trackIds.map((id, i) => (
|
||||
<TrackCell key={id} id={id} onMouseDown={this._handleClick}>
|
||||
<TrackCell key={id} id={id} index={i} onMouseDown={this._handleClick}>
|
||||
{format(id, i)}
|
||||
</TrackCell>
|
||||
));
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
SET_MEDIA,
|
||||
CLICKED_TRACK,
|
||||
CTRL_CLICKED_TRACK,
|
||||
SHIFT_CLICKED_TRACK,
|
||||
SELECT_ALL,
|
||||
SELECT_ZERO,
|
||||
INVERT_SELECTION,
|
||||
|
|
@ -21,27 +22,49 @@ import { shuffle, moveSelected, mapObject, filterObject } from "../utils";
|
|||
const defaultPlaylistState = {
|
||||
trackOrder: [],
|
||||
currentTrack: null,
|
||||
tracks: {}
|
||||
tracks: {},
|
||||
lastSelectedIndex: null
|
||||
};
|
||||
|
||||
const playlist = (state = defaultPlaylistState, action) => {
|
||||
switch (action.type) {
|
||||
case CLICKED_TRACK:
|
||||
const clickedId = String(state.trackOrder[action.index]);
|
||||
return {
|
||||
...state,
|
||||
tracks: mapObject(state.tracks, (track, id) => ({
|
||||
...track,
|
||||
selected: id === String(action.id)
|
||||
}))
|
||||
selected: id === clickedId
|
||||
})),
|
||||
lastSelectedIndex: action.index
|
||||
};
|
||||
case CTRL_CLICKED_TRACK:
|
||||
const t = state.tracks[action.id];
|
||||
const id = state.trackOrder[action.index];
|
||||
const t = state.tracks[id];
|
||||
return {
|
||||
...state,
|
||||
tracks: {
|
||||
...state.tracks,
|
||||
[action.id]: { ...t, selected: !t.selected }
|
||||
}
|
||||
[id]: { ...t, selected: !t.selected }
|
||||
},
|
||||
// Using this as the lastClickedIndex is kinda funny, since you
|
||||
// may have just _un_selected the track. However, this is what
|
||||
// Winamp 2 does, so we'll copy it.
|
||||
lastSelectedIndex: action.index
|
||||
};
|
||||
case SHIFT_CLICKED_TRACK:
|
||||
if (state.lastSelectedIndex == null) {
|
||||
return state;
|
||||
}
|
||||
const clickedIndex = action.index;
|
||||
const start = Math.min(clickedIndex, state.lastSelectedIndex);
|
||||
const end = Math.max(clickedIndex, state.lastSelectedIndex);
|
||||
return {
|
||||
...state,
|
||||
tracks: state.trackOrder.map((trackId, i) => ({
|
||||
...state.tracks[trackId],
|
||||
selected: i >= start && i <= end
|
||||
}))
|
||||
};
|
||||
case SELECT_ALL:
|
||||
return {
|
||||
|
|
@ -66,24 +89,36 @@ const playlist = (state = defaultPlaylistState, action) => {
|
|||
};
|
||||
case REMOVE_ALL_TRACKS:
|
||||
// TODO: Consider disposing of ObjectUrls
|
||||
return { ...state, trackOrder: [], currentTrack: null, tracks: {} };
|
||||
return {
|
||||
...state,
|
||||
trackOrder: [],
|
||||
currentTrack: null,
|
||||
tracks: {},
|
||||
lastSelectedIndex: null
|
||||
};
|
||||
case REMOVE_TRACKS:
|
||||
// TODO: Consider disposing of ObjectUrls
|
||||
const actionIds = action.ids.map(Number);
|
||||
const { currentTrack } = state;
|
||||
return {
|
||||
...state,
|
||||
trackOrder: state.trackOrder.filter(id => !actionIds.includes(id)),
|
||||
trackOrder: state.trackOrder.filter(
|
||||
trackId => !actionIds.includes(trackId)
|
||||
),
|
||||
currentTrack: actionIds.includes(currentTrack) ? null : currentTrack,
|
||||
tracks: filterObject(
|
||||
state.tracks,
|
||||
(track, id) => !action.ids.includes(id)
|
||||
)
|
||||
(track, trackId) => !action.ids.includes(trackId)
|
||||
),
|
||||
// TODO: This could probably be made to work, but we clear it just to be safe.
|
||||
lastSelectedIndex: null
|
||||
};
|
||||
case REVERSE_LIST:
|
||||
return {
|
||||
...state,
|
||||
trackOrder: [...state.trackOrder].reverse()
|
||||
trackOrder: [...state.trackOrder].reverse(),
|
||||
// TODO: This could probably be made to work, but we clear it just to be safe.
|
||||
lastSelectedIndex: null
|
||||
};
|
||||
case RANDOMIZE_LIST:
|
||||
return {
|
||||
|
|
@ -106,7 +141,9 @@ const playlist = (state = defaultPlaylistState, action) => {
|
|||
duration: null,
|
||||
url: action.url
|
||||
}
|
||||
}
|
||||
},
|
||||
// TODO: This could probably be made to work, but we clear it just to be safe.
|
||||
lastSelectedIndex: null
|
||||
};
|
||||
case SET_MEDIA:
|
||||
return {
|
||||
|
|
@ -141,12 +178,11 @@ const playlist = (state = defaultPlaylistState, action) => {
|
|||
...state,
|
||||
trackOrder: moveSelected(
|
||||
state.trackOrder,
|
||||
i => {
|
||||
const id = state.trackOrder[i];
|
||||
return state.tracks[id].selected;
|
||||
},
|
||||
i => state.tracks[state.trackOrder[i]].selected,
|
||||
action.offset
|
||||
)
|
||||
),
|
||||
// TODO: This could probably be made to work, but we clear it just to be safe.
|
||||
lastSelectedIndex: null
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue