Skip to content

Commit

Permalink
closes #733: right click on strand name or domain name brings up stra…
Browse files Browse the repository at this point in the history
…nd context menu
  • Loading branch information
dave-doty committed Jan 19, 2022
1 parent a5a54a7 commit 6be2d47
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 17 deletions.
3 changes: 2 additions & 1 deletion lib/src/view/design_main_strand.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ class DesignMainStrandComponent extends UiComponent2<DesignMainStrandProps>
..only_display_selected_helices = props.only_display_selected_helices
..show_domain_names = props.show_domain_names
..show_strand_names = props.show_strand_names
..helix_idx_to_svg_position_y_map = helix_idx_to_svg_position_y_map_on_strand
..context_menu_strand = context_menu_strand
..helix_idx_to_svg_position = helix_idx_to_svg_position_y_map_on_strand
..domain_name_font_size = props.domain_name_font_size
..strand_name_font_size = props.strand_name_font_size
..key = 'domain-names')(),
Expand Down
23 changes: 17 additions & 6 deletions lib/src/view/design_main_strand_and_domain_names.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import 'dart:math';

import 'package:built_collection/built_collection.dart';
import 'package:over_react/over_react.dart';
import 'package:scadnano/src/state/address.dart';
import 'package:scadnano/src/state/context_menu.dart';
import 'package:scadnano/src/state/modification_type.dart';

import 'transform_by_helix_group.dart';
import '../state/geometry.dart';
Expand Down Expand Up @@ -39,7 +42,10 @@ mixin DesignMainStrandAndDomainNamesPropsMixin on UiProps {
int domain_name_font_size;
int strand_name_font_size;

BuiltMap<int, Point<num>> helix_idx_to_svg_position_y_map;
BuiltMap<int, Point<num>> helix_idx_to_svg_position;

List<ContextMenuItem> Function(Strand strand, {Domain domain, Address address, ModificationType type})
context_menu_strand;
}

class DesignMainStrandAndDomainNamesProps = UiProps
Expand Down Expand Up @@ -89,21 +95,23 @@ class DesignMainStrandAndDomainNamesComponent extends UiComponent2<DesignMainStr
bool draw_domain = should_draw_domain(
domain_5p.helix, props.side_selected_helix_idxs, props.only_display_selected_helices);

var helix_svg_position = props.helix_idx_to_svg_position_y_map[domain_5p.helix];
var helix_svg_position = props.helix_idx_to_svg_position[domain_5p.helix];

// don't bother if 5' domain is not visible; if we give more sophisticated options for where to place
// the strand name later, this should be changed
if (draw_domain && props.strand.name != null) {
Helix helix = props.helices[domain_5p.helix];
strand_name_component = (DesignMainStrandStrandName()
..strand_name = props.strand.name
..strand = props.strand
..domain = domain_5p
..helix = helix
..helix_groups = props.groups
..geometry = props.geometry
..font_size = props.strand_name_font_size
..transform = transform_of_helix(domain_5p.helix)
..helix_svg_position_y = helix_svg_position.y
..helix_svg_position = helix_svg_position
..show_dna = props.show_dna
..context_menu_strand = props.context_menu_strand
..show_domain_names = props.show_domain_names
..className = constants.css_selector_strand_name
..key = "strand-name")();
Expand All @@ -121,15 +129,18 @@ class DesignMainStrandAndDomainNamesComponent extends UiComponent2<DesignMainStr
domain.helix, props.side_selected_helix_idxs, props.only_display_selected_helices);
if (draw_domain && domain.name != null) {
Helix helix = props.helices[substrand.helix];
var helix_svg_position = props.helix_idx_to_svg_position_y_map[substrand.helix];
var helix_svg_position = props.helix_idx_to_svg_position[substrand.helix];
names.add((DesignMainStrandDomainName()
..strand = props.strand
..domain = substrand
..helix = helix
..helix_groups = props.groups
..geometry = props.geometry
..font_size = props.domain_name_font_size
..transform = transform_of_helix(domain.helix)
..svg_position_y = helix_svg_position.y
..helix_svg_position = helix_svg_position
..show_dna = props.show_dna
..context_menu_strand = props.context_menu_strand
..className = constants.css_selector_domain_name
..key = "domain-name-$i")());
}
Expand Down
63 changes: 58 additions & 5 deletions lib/src/view/design_main_strand_domain_name.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,46 @@
import 'dart:html';
import 'dart:math';

import 'package:meta/meta.dart';
import 'package:over_react/over_react.dart';
import 'package:built_collection/built_collection.dart';

import '../state/strand.dart';
import '../state/address.dart';
import '../state/geometry.dart';
import '../state/group.dart';
import '../app.dart';
import '../state/helix.dart';
import '../state/domain.dart';
import '../util.dart' as util;
import '../state/selectable.dart';
import 'design_main_strand_dna_end.dart';
import 'pure_component.dart';
import '../state/helix.dart';
import '../state/context_menu.dart';
import '../state/modification_type.dart';
import '../actions/actions.dart' as actions;
import '../constants.dart' as constants;

part 'design_main_strand_domain_name.over_react.g.dart';

UiFactory<DesignMainStrandDomainNameProps> DesignMainStrandDomainName = _$DesignMainStrandDomainName;

mixin DesignMainStrandDomainNamePropsMixin on UiProps {
Strand strand;
Domain domain;
Helix helix;
Geometry geometry;
BuiltMap<String, HelixGroup> helix_groups;

int font_size;
bool show_dna;
String transform;
num svg_position_y;
Point<num> helix_svg_position;

List<ContextMenuItem> Function(Strand strand,
{@required Domain domain,
@required Address address,
@required ModificationType type}) context_menu_strand;
}

class DesignMainStrandDomainNameProps = UiProps with DesignMainStrandDomainNamePropsMixin;
Expand All @@ -29,9 +49,10 @@ class DesignMainStrandDomainNameComponent extends UiComponent2<DesignMainStrandD
with PureComponent {
@override
render() {

Point<num> start_svg = props.helix.svg_base_pos(props.domain.start, props.domain.forward, props.svg_position_y);
Point<num> end_svg = props.helix.svg_base_pos(props.domain.end - 1, props.domain.forward, props.svg_position_y);
Point<num> start_svg =
props.helix.svg_base_pos(props.domain.start, props.domain.forward, props.helix_svg_position.y);
Point<num> end_svg =
props.helix.svg_base_pos(props.domain.end - 1, props.domain.forward, props.helix_svg_position.y);
Point<num> mid_svg = (start_svg + end_svg) * 0.5;

String baseline = props.domain.forward ? 'baseline' : 'hanging';
Expand All @@ -48,9 +69,41 @@ class DesignMainStrandDomainNameComponent extends UiComponent2<DesignMainStrandD
..x = '${mid_svg.x}'
..y = '${mid_svg.y}'
..dy = '${dy}'
..id = id()
..transform = props.transform
..fontSize = props.font_size
..dominantBaseline = baseline
..className = constants.css_selector_domain_name_text)(props.domain.name);
}

String id() => props.domain.id + '_name';

// needed for capturing right-click events with React:
// https://medium.com/@ericclemmons/react-event-preventdefault-78c28c950e46
@override
componentDidMount() {
var element = querySelector('#${id()}');
element.addEventListener('contextmenu', on_context_menu);
}

@override
componentWillUnmount() {
var element = querySelector('#${id()}');
element.removeEventListener('contextmenu', on_context_menu);
super.componentWillUnmount();
}

on_context_menu(Event ev) {
MouseEvent event = ev;
if (!event.shiftKey) {
event.preventDefault();
event.stopPropagation();
Address address = util.find_closest_address(event, [props.helix], props.helix_groups,
props.geometry, {props.helix.idx: props.helix_svg_position}.build());
app.dispatch(actions.ContextMenuShow(
context_menu: ContextMenu(
items: props.context_menu_strand(props.strand, domain: props.domain, address: address).build(),
position: event.page)));
}
}
}
59 changes: 54 additions & 5 deletions lib/src/view/design_main_strand_strand_name.dart
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
import 'dart:html';
import 'dart:math';

import 'package:meta/meta.dart';
import 'package:over_react/over_react.dart';
import 'package:built_collection/built_collection.dart';

import '../state/group.dart';
import '../state/geometry.dart';
import '../state/domain.dart';
import 'pure_component.dart';
import '../state/helix.dart';
import '../state/context_menu.dart';
import '../state/strand.dart';
import '../state/address.dart';
import '../state/modification_type.dart';
import '../app.dart';
import '../constants.dart' as constants;
import '../util.dart' as util;
import '../actions/actions.dart' as actions;

part 'design_main_strand_strand_name.over_react.g.dart';

UiFactory<DesignMainStrandStrandNameProps> DesignMainStrandStrandName = _$DesignMainStrandStrandName;

mixin DesignMainStrandStrandNamePropsMixin on UiProps {
String strand_name;
Strand strand;
Domain domain; // domain next to which we draw strand name
Helix helix;
Geometry geometry;
BuiltMap<String, HelixGroup> helix_groups;

int font_size;
bool show_dna;
bool show_domain_names;
String transform;
num helix_svg_position_y;
Point<num> helix_svg_position;

List<ContextMenuItem> Function(Strand strand,
{@required Domain domain,
@required Address address,
@required ModificationType type}) context_menu_strand;
}

class DesignMainStrandStrandNameProps = UiProps with DesignMainStrandStrandNamePropsMixin;
Expand All @@ -32,9 +49,9 @@ class DesignMainStrandStrandNameComponent extends UiComponent2<DesignMainStrandS
@override
render() {
Point<num> start_svg =
props.helix.svg_base_pos(props.domain.start, props.domain.forward, props.helix_svg_position_y);
props.helix.svg_base_pos(props.domain.start, props.domain.forward, props.helix_svg_position.y);
Point<num> end_svg =
props.helix.svg_base_pos(props.domain.end - 1, props.domain.forward, props.helix_svg_position_y);
props.helix.svg_base_pos(props.domain.end - 1, props.domain.forward, props.helix_svg_position.y);
Point<num> mid_svg = (start_svg + end_svg) * 0.5;

String baseline = props.domain.forward ? 'baseline' : 'hanging';
Expand All @@ -60,6 +77,38 @@ class DesignMainStrandStrandNameComponent extends UiComponent2<DesignMainStrandS
..transform = props.transform
..fontSize = props.font_size
..dominantBaseline = baseline
..className = constants.css_selector_strand_name_text)(props.strand_name);
..id = id()
..className = constants.css_selector_strand_name_text)(props.strand.name);
}

String id() => props.strand.id + '_name';

// needed for capturing right-click events with React:
// https://medium.com/@ericclemmons/react-event-preventdefault-78c28c950e46
@override
componentDidMount() {
var element = querySelector('#${id()}');
element.addEventListener('contextmenu', on_context_menu);
}

@override
componentWillUnmount() {
var element = querySelector('#${id()}');
element.removeEventListener('contextmenu', on_context_menu);
super.componentWillUnmount();
}

on_context_menu(Event ev) {
MouseEvent event = ev;
if (!event.shiftKey) {
event.preventDefault();
event.stopPropagation();
Address address = util.find_closest_address(event, [props.helix], props.helix_groups, props.geometry,
{props.helix.idx: props.helix_svg_position}.build());
app.dispatch(actions.ContextMenuShow(
context_menu: ContextMenu(
items: props.context_menu_strand(props.strand, domain: props.domain, address: address).build(),
position: event.page)));
}
}
}

0 comments on commit 6be2d47

Please sign in to comment.