Handling a function which ouput relies on child states

KeyCode::Enter => {
    match self.player.focused_widget {
        Focus::Tracklist => {
            match self.player.mode {
                Mode::Default => {
                    if let Some((_, selected_list)) =
                        &self.player.tracklist.selected_playlist
                    {
                        self.player.tracklist.base = selected_list.clone();
                    } else {
                        self.player.tracklist.base = self.tracks.clone();
                    };

                    let index = match self.shuffle {
                        Shuffle::Random => {
                            self.player.tracklist.list =
                                self.player.tracklist.base.clone();

                            let index = (self.player.tracklist.cursor
                                + self.player.tracklist.y_offset)
                                as usize;

                            self.player.tracklist.list.swap(index, 0);
                            self.player.tracklist.list[1..]
                                .shuffle(&mut rand::rng());

                            0
                        }
                        _ => {
                            self.player.tracklist.list =
                                self.player.tracklist.base.clone();

                            let index = (self.player.tracklist.cursor
                                + self.player.tracklist.y_offset)
                                as usize;

                            index
                        }
                    };

                    self.player.set_auto_queue(index);

                    let track = self.player.pop_auto_queue().unwrap();

                    self.play(track);
                }
                Mode::Select => {}
            }
        }
        Focus::Playlist => match self.player.mode {
            Mode::Default => {
                let playlist = self.player.playlist.get_under_cursor();
                let db_tracks =
                    DbTrack::get_by_playlist_uuid(&self.db_conn, playlist.uuid);

                let tracks = Track::from_db_tracks(&self.tracks, db_tracks);

                self.player.tracklist.selected_playlist =
                    Some((playlist, tracks));

                self.player.switch_window();
                self.player.tracklist.y_offset = 0;
                self.player.tracklist.cursor = 0;
            }
            Mode::Select => {
                let playlist = self.player.playlist.get_under_cursor();
                let db_tracks =
                    DbTrack::get_by_playlist_uuid(&self.db_conn, playlist.uuid);

                let tracks = Track::from_db_tracks(&self.tracks, db_tracks);

                let selected_track =
                    self.player.playlist.selected_track.take().unwrap();

                if tracks
                    .iter()
                    .find(|t| t.uuid == selected_track.uuid)
                    .is_some()
                {
                    self.player.mode = Mode::Default;
                    self.player.switch_window();
                    return;
                }

                selected_track
                    .insert_into_playlist(playlist.uuid, &self.db_conn);

                self.player.mode = Mode::Default;
                self.player.switch_window();
            }
        },
    }
}

I have parent widget App that handles all inputs and resolves anything related to other threads and database. It has main component Player. Player has at least 4 states for now. 2 are focused widgets and 2 are modes (Select or Default).

All of them need to do something different when enter is pressed.

Focus::Tracklist with Mode::Default pops new audio to play it on a different thread. The rest doesn’t return anything. But they can fail, and in future i want to handle those too.

I’ve tried to make dispatch function, but it looks like i have to use Message enum. And now i don’t need it anywhere but Enter handling. So it would be overkill probably. I might be wrong here.
I’ve considered how iced approached it.
The downsides are:

  1. Majority of events will return Message::None
  2. Event loop will have to read event, call the function and always match the message, returned from function.

For now i’m handling it directly in App. It wouldn’t be a problem, but there are multiple places where i need to do something that relies on state of Player, so in every place like that i need to do nested matches. And i only started to implement functionality. In future there will be even more places where i need to do the same. But amount of places where Message::None is returned will increase as well.

My questions are:

  1. Does Message approach gives more upsides than downsides?
  2. How else can i handle it if i don’t want to use Message?
  3. Maybe i should give up on centralized event reading? But in that case i don’t know other patterns to make decentralized system.

On second thought i might be wrong in the app architecture. I tried to reuse functionality of my widgets, but ended up with widgets that are doing some things using optional properties. That might be wrong.

If you’re interested here’s link
The files i’m referencing are src/app.rs, src/screen/player.rs/, src/widget/tracklist.rs and src/widget/playlist.rs