From 945ff2c57f848954af7c4a0e9244e5e3a2f3bd27 Mon Sep 17 00:00:00 2001 From: David Doty Date: Thu, 19 Sep 2024 15:07:23 -0400 Subject: [PATCH] fixes #992: adding keyboard shortcuts for select mode change --- lib/src/state/select_mode.dart | 193 ++++++++++++--------------------- lib/src/view/design.dart | 27 +++++ lib/src/view/menu.dart | 69 ------------ 3 files changed, 98 insertions(+), 191 deletions(-) diff --git a/lib/src/state/select_mode.dart b/lib/src/state/select_mode.dart index 5eff0dd3e..3823c0caa 100644 --- a/lib/src/state/select_mode.dart +++ b/lib/src/state/select_mode.dart @@ -92,162 +92,110 @@ class SelectModeChoice extends EnumClass { switch (this) { case end_5p_strand: return '''\ -5' strand: Allows one to select the 5' -end (square) of a whole strand. If many -5' ends are selected, then one can -add a 5' modification to all of them -by right-clicking and selecting "add -modification". This will add only to -the type of modification picked. For -example, if both 5' and 3' ends are -selected, and a 5' modification is -added, then only the 5' ends are +5' strand: Allows one to select the 5' end (square) of a whole strand. If many +5' ends are selected, then one can add a 5' modification to all of them +by right-clicking and selecting "add modification". This will add only to +the type of modification picked. For example, if both 5' and 3' ends are +selected, and a 5' modification is added, then only the 5' ends are modified.'''; case end_3p_strand: return '''\ -3' strand: Allows one to select the 3' -end (triangle) of a whole strand. If many -3' ends are selected, then one can -add a 3' modification to all of them -by right-clicking and selecting "add -modification". This will add only to -the type of modification picked. For -example, if both 5' and 3' ends are -selected, and a 3' modification is -added, then only the 3' ends are +3' strand: Allows one to select the 3' end (triangle) of a whole strand. If +many 3' ends are selected, then one can add a 3' modification to all of them +by right-clicking and selecting "add modification". This will add only to +the type of modification picked. For example, if both 5' and 3' ends are +selected, and a 3' modification is added, then only the 3' ends are modified.'''; case end_5p_domain: return '''\ -5' domain: Each strand is composed of -one or more bound domains, defined to -be a portion of a strand that exists on -a single helix. A 5'/3' end of a bound -domain that is not the 5'/3' end of the -whole strand is one of these. They are -not normally visible, but when these -select modes are enabled, they become -visible on mouseover and can be -selected and dragged. Deleting a 5'/3' -end of a bound domain deletes the whole -bound domain. Ends can be moved, but -unlike strands and domains, they can -only be moved back and forth along their +5' domain: Each strand is composed of one or more bound domains, defined to +be a portion of a strand that exists on a single helix. A 5'/3' end of a bound +domain that is not the 5'/3' end of the whole strand is one of these. They are +not normally visible, but when these select modes are enabled, they become +visible on mouseover and can be selected and dragged. Deleting a 5'/3' +end of a bound domain deletes the whole bound domain. Ends can be moved, but +unlike strands and domains, they can only be moved back and forth along their current helix.'''; case end_3p_domain: return '''\ -3' domain: Each strand is composed of -one or more bound domains, defined to -be a portion of a strand that exists on -a single helix. A 5'/3' end of a bound -domain that is not the 5'/3' end of the -whole strand is one of these. They are -not normally visible, but when these -select modes are enabled, they become -visible on mouseover and can be -selected and dragged. Deleting a 5'/3' -end of a bound domain deletes the whole -bound domain. Ends can be moved, but -unlike strands and domains, they can -only be moved back and forth along their +3' domain: Each strand is composed of one or more bound domains, defined to +be a portion of a strand that exists on a single helix. A 5'/3' end of a bound +domain that is not the 5'/3' end of the whole strand is one of these. They are +not normally visible, but when these select modes are enabled, they become +visible on mouseover and can be selected and dragged. Deleting a 5'/3' +end of a bound domain deletes the whole bound domain. Ends can be moved, but +unlike strands and domains, they can only be moved back and forth along their current helix.'''; case domain: return '''\ -domain: A single bound domain can be -selected. Groups of domains can be -moved, but only if they are all in the -same helix group. (Though they can be -moved to a different helix group.)'''; +domain: A single bound domain can be selected. Groups of domains can be +moved, but only if they are all in the same helix group. (Though they can be +moved to a different helix group.) +[Alt+O or Alt+D (Alt+D has another behavior in Chrome that pops up a window)]'''; case crossover: return '''\ -crossover: Two consecutive bound -domains on a strand can be joined by a -crossover, which consists of no DNA -bases (Technically bound domains do not -have to be bound to another strand, but -the idea is that generally in a -finished design, most of the bound -domains will actually be bound to +crossover: Two consecutive bound domains on a strand can be joined by a +crossover, which consists of no DNA bases (Technically bound domains do not +have to be bound to another strand, but the idea is that generally in a +finished design, most of the bound domains will actually be bound to another.) -If many crossovers/loopouts are -selected, all the crossovers can be -converted to loopouts (or vice versa) -by right-clicking on one of them and -picking "convert to loopout" (or -"change loopout length" if a loopout; -changing to length 0 converts it to a -crossover).'''; +If many crossovers/loopouts are selected, all the crossovers can be +converted to loopouts (or vice versa) by right-clicking on one of them and +picking "convert to loopout" (or "change loopout length" if a loopout; +changing to length 0 converts it to a crossover). +[Alt+C]'''; case loopout: return '''\ -loopout: Two consecutive bound domains -on a strand can be joined by a loopout, -which is a single-stranded portion of -the strand with one or more DNA bases. -(Technically bound domains do not have -to be bound to another strand, but the -idea is that generally in a finished -design, most of the bound domains will +loopout: Two consecutive bound domains on a strand can be joined by a loopout, +which is a single-stranded portion of the strand with one or more DNA bases. +(Technically bound domains do not have to be bound to another strand, but the +idea is that generally in a finished design, most of the bound domains will actually be bound to another.) -If many crossovers/loopouts are -selected, all the crossovers can be -converted to loopouts (or vice versa) -by right-clicking on one of them and -picking "convert to loopout" (or -"change loopout length" if a loopout; -changing to length 0 converts it to a -crossover).'''; +If many crossovers/loopouts are selected, all the crossovers can be +converted to loopouts (or vice versa) by right-clicking on one of them and +picking "convert to loopout" (or "change loopout length" if a loopout; +changing to length 0 converts it to a crossover). +[Alt+L]'''; case extension_: return '''\ -extension: An extension is a single-stranded -portion of a strand that is not on a -Helix (like a domain) and does not connect -two domains. It is like a loopout but on -the end of a strand, useful for modeling -toeholds for DNA strand displacement, -for instance.'''; +extension: An extension is a single-stranded portion of a strand that is not on a +Helix (like a domain) and does not connect two domains. It is like a loopout but on +the end of a strand, useful for modeling toeholds for DNA strand displacement, +for instance. +[Alt+X]'''; case deletion: return '''\ -deletion: Deletions can be selected and -deleted in batch by pressing the Delete key.'''; +deletion: Deletions can be selected and deleted in batch by pressing the Delete key.'''; case insertion: return '''\ -insertion: Insertions can be selected -and deleted in batch by pressing the -Delete key. Also, one can change the -length of all selected insertions by -right-clicking on one of them and -selecting the option to change -insertion length.'''; +insertion: Insertions can be selected and deleted in batch by pressing the +Delete key. Also, one can change the length of all selected insertions by +right-clicking on one of them and selecting the option to change +insertion length. +[Alt+I]'''; case modification: return '''\ -modification: If many modifications are -selected, they can be deleted at once -by pressing the Delete key (or -right-clicking and selecting "remove -modification"). Those of a similar type -(5', 3', or internal) can be modified -in batch by right-clicking on one of -them and selecting "edit -modification".'''; +modification: If many modifications are selected, they can be deleted at once +by pressing the Delete key (or right-clicking and selecting "remove +modification"). Those of a similar type (5', 3', or internal) can be modified +in batch by right-clicking on one of them and selecting "edit modification". +[Alt+M]'''; case strand: return '''\ -strand: The whole strand can be selected. -Groups of strands can be copy/pasted or -moved, but only if they are all in the -same helix group. (Though they can be -copied/moved to a different helix group.)'''; +strand: The whole strand can be selected. Groups of strands can be copy/pasted or +moved, but only if they are all in the same helix group. (Though they can be +copied/moved to a different helix group.) +[Alt+S]'''; case scaffold: return '''\ -scaffold: This option allows one to -select scaffold strands. The option is +scaffold: This option allows one to select scaffold strands. The option is not shown in a non-origami design.'''; case staple: return '''\ -staple: All non-scaffold strands are -called staples. This option allows one -to select staples. The option is not -shown in a non-origami design.'''; +staple: All non-scaffold strands are called staples. This option allows one +to select staples. The option is not shown in a non-origami design.'''; } return ''; } @@ -255,7 +203,8 @@ shown in a non-origami design.'''; static String get all_ends_image_file => 'images/select_mode_icons/allends.svg'; static String get all_ends_tooltip => '''all ends: Selects all of 5' strand, 3' -strand, 5' domain, 3' domain.'''; +strand, 5' domain, 3' domain. +[Alt+E]'''; String css_selector() { switch (this) { diff --git a/lib/src/view/design.dart b/lib/src/view/design.dart index 94634bf04..67a24ed6b 100644 --- a/lib/src/view/design.dart +++ b/lib/src/view/design.dart @@ -25,6 +25,7 @@ import '../state/address.dart'; import '../state/dna_ends_move.dart'; import '../state/edit_mode.dart'; import '../state/helix.dart'; +import '../state/select_mode.dart'; import '../state/strand_creation.dart'; import '../state/strands_move.dart'; @@ -637,6 +638,32 @@ class DesignViewComponent { // Alt+Shift+A for Select all with same ev.preventDefault(); app.disable_keyboard_shortcuts_while(ask_for_select_all_with_same_as_selected); + } else if ((app.state.ui_state.edit_modes.contains(EditModeChoice.select) || + app.state.ui_state.edit_modes.contains(EditModeChoice.rope_select) && + ev.altKey && + !(ev.ctrlKey || ev.metaKey))) { + // Alt+? for select modes + ev.preventDefault(); // for some reason this is not stopping Alt+D, + // so we also let the user type Alt+O since Alt+D has a special meaning in Chrome + if (key == KeyCode.S) { + app.dispatch(actions.SelectModeToggle(SelectModeChoice.strand)); + } else if (key == KeyCode.O || key == KeyCode.D) { + app.dispatch(actions.SelectModeToggle(SelectModeChoice.domain)); + } else if (key == KeyCode.E) { + app.dispatch(actions.SelectModesAdd(modes: SelectModeChoice.ends)); + } else if (key == KeyCode.C) { + app.dispatch(actions.SelectModeToggle(SelectModeChoice.crossover)); + } else if (key == KeyCode.L) { + app.dispatch(actions.SelectModeToggle(SelectModeChoice.loopout)); + } else if (key == KeyCode.X) { + app.dispatch(actions.SelectModeToggle(SelectModeChoice.extension_)); + } else if (key == KeyCode.I) { + app.dispatch(actions.SelectModeToggle(SelectModeChoice.insertion)); + } else if (key == KeyCode.N) { + app.dispatch(actions.SelectModeToggle(SelectModeChoice.deletion)); + } else if (key == KeyCode.M) { + app.dispatch(actions.SelectModeToggle(SelectModeChoice.modification)); + } } if (key == EditModeChoice.pencil.key_code()) { diff --git a/lib/src/view/menu.dart b/lib/src/view/menu.dart index a33f01e8c..6767b0a0a 100644 --- a/lib/src/view/menu.dart +++ b/lib/src/view/menu.dart @@ -48,75 +48,6 @@ UiFactory ConnectedMenu = connect( forwardRef: true, )(Menu); -/* -mapStateToProps: (AppState state) { - return (Menu() - ..selected_ends = state.ui_state.selectables_store.selected_dna_ends - ..geometry = state.maybe_design?.geometry - ..no_grid_is_none = state.maybe_design == null - ? false - : state.design.groups.values.every((group) => group.grid != Grid.none) - ..show_oxview = state.ui_state.show_oxview - ..show_dna = state.ui_state.show_dna - ..base_pair_display_type = state.ui_state.base_pair_display_type - ..show_strand_names = state.ui_state.show_strand_names - ..show_strand_labels = state.ui_state.show_strand_labels - ..show_domain_names = state.ui_state.show_domain_names - ..show_domain_labels = state.ui_state.show_domain_labels - ..strand_name_font_size = state.ui_state.strand_name_font_size - ..strand_label_font_size = state.ui_state.strand_label_font_size - ..domain_name_font_size = state.ui_state.domain_name_font_size - ..domain_label_font_size = state.ui_state.domain_label_font_size - ..show_modifications = state.ui_state.show_modifications - ..show_mismatches = state.ui_state.show_mismatches - ..show_domain_name_mismatches = state.ui_state.show_domain_name_mismatches - ..show_unpaired_insertion_deletions = state.ui_state.show_unpaired_insertion_deletions - ..strand_paste_keep_color = state.ui_state.strand_paste_keep_color - ..zoom_speed = state.ui_state.zoom_speed - ..autofit = state.ui_state.autofit - ..only_display_selected_helices = state.ui_state.only_display_selected_helices - ..show_base_pair_lines = state.ui_state.show_base_pair_lines - ..show_base_pair_lines_with_mismatches = state.ui_state.show_base_pair_lines_with_mismatches - ..example_designs = state.ui_state.example_designs - ..design_has_insertions_or_deletions = state.maybe_design?.has_insertions_or_deletions == true - ..undo_stack_empty = state.undo_redo.undo_stack.isEmpty - ..redo_stack_empty = state.undo_redo.redo_stack.isEmpty - ..enable_copy = app.state.ui_state.selectables_store.selected_strands.isNotEmpty - ..modification_font_size = state.ui_state.modification_font_size - ..major_tick_offset_font_size = state.ui_state.major_tick_offset_font_size - ..major_tick_width_font_size = state.ui_state.major_tick_width_font_size - ..modification_display_connector = state.ui_state.modification_display_connector - ..display_of_major_ticks_offsets = state.ui_state.display_base_offsets_of_major_ticks - ..display_base_offsets_of_major_ticks_only_first_helix = - state.ui_state.display_base_offsets_of_major_ticks_only_first_helix - ..display_major_tick_widths = state.ui_state.display_major_tick_widths - ..display_major_tick_widths_all_helices = state.ui_state.display_major_tick_widths_all_helices - ..invert_y = state.ui_state.invert_y - ..dynamically_update_helices = state.ui_state.dynamically_update_helices - ..show_helix_circles_main_view = state.ui_state.show_helix_circles_main_view - ..show_helix_components_main_view = state.ui_state.show_helix_components_main_view - ..warn_on_exit_if_unsaved = state.ui_state.warn_on_exit_if_unsaved - ..show_grid_coordinates_side_view = state.ui_state.show_grid_coordinates_side_view - ..show_helices_axis_arrows = state.ui_state.show_helices_axis_arrows - ..show_loopout_extension_length = state.ui_state.show_loopout_extension_length - ..export_svg_text_separately = state.ui_state.export_svg_text_separately - ..show_slice_bar = state.ui_state.show_slice_bar - ..show_mouseover_data = state.ui_state.show_mouseover_data - ..disable_png_caching_dna_sequences = state.ui_state.disable_png_caching_dna_sequences - ..retain_strand_color_on_selection = state.ui_state.retain_strand_color_on_selection - ..display_reverse_DNA_right_side_up = state.ui_state.display_reverse_DNA_right_side_up - ..local_storage_design_choice = state.ui_state.local_storage_design_choice - ..clear_helix_selection_when_loading_new_design = - state.ui_state.clear_helix_selection_when_loading_new_design - ..default_crossover_type_scaffold_for_setting_helix_rolls = - state.ui_state.default_crossover_type_scaffold_for_setting_helix_rolls - ..default_crossover_type_staple_for_setting_helix_rolls = - state.ui_state.default_crossover_type_staple_for_setting_helix_rolls - ..selection_box_intersection = state.ui_state.selection_box_intersection - ..ox_export_only_selected_strands = state.ui_state.ox_export_only_selected_strands - ..undo_redo = state.undo_redo); - */ - UiFactory Menu = _$Menu; mixin MenuProps on UiProps {