Extract shape topics as classes.

Fix bugs on color connection render
This commit is contained in:
Paulo Gustavo Veiga 2022-12-31 12:45:15 -08:00
parent f6b7d19cb1
commit 7977fcee42
10 changed files with 179 additions and 93 deletions

View File

@ -30,7 +30,7 @@ class CentralTopic extends Topic {
// This disable the drag of the central topic. // This disable the drag of the central topic.
// But solves the problem of deselecting the nodes when the screen is clicked. // But solves the problem of deselecting the nodes when the screen is clicked.
this.addEvent('mousedown', (event) => { this.addEvent('mousedown', (event: MouseEvent) => {
event.stopPropagation(); event.stopPropagation();
}); });
} }

View File

@ -89,7 +89,12 @@ class ConnectionLine {
} }
private updateColor(): void { private updateColor(): void {
const color = this._targetTopic.getConnectionColor(); // In case that the main topic has changed the color, overwrite the main topic definiton.
let color = this._targetTopic.getConnectionColor();
if (this._targetTopic.isCentralTopic()) {
color = this._sourceTopic.getModel().getConnectionColor() || color;
}
this._color = color; this._color = color;
switch (this._lineType) { switch (this._lineType) {
case LineType.POLYLINE_MIDDLE: case LineType.POLYLINE_MIDDLE:

View File

@ -23,11 +23,13 @@ import Shape from './util/Shape';
import NodeModel from './model/NodeModel'; import NodeModel from './model/NodeModel';
import Workspace from './Workspace'; import Workspace from './Workspace';
import SizeType from './SizeType'; import SizeType from './SizeType';
import PositionType from './PositionType';
import { NodeOption } from './NodeGraph';
class MainTopic extends Topic { class MainTopic extends Topic {
private INNER_RECT_ATTRIBUTES: { stroke: string }; private INNER_RECT_ATTRIBUTES: { stroke: string };
constructor(model: NodeModel, options) { constructor(model: NodeModel, options: NodeOption) {
super(model, options); super(model, options);
this.INNER_RECT_ATTRIBUTES = { stroke: '0.5 solid #009900' }; this.INNER_RECT_ATTRIBUTES = { stroke: '0.5 solid #009900' };
} }
@ -112,11 +114,11 @@ class MainTopic extends Topic {
} }
} }
workoutIncomingConnectionPoint(sourcePosition: Point) { workoutIncomingConnectionPoint(sourcePosition: PositionType): PositionType {
return Shape.workoutIncomingConnectionPoint(this, sourcePosition); return Shape.workoutIncomingConnectionPoint(this, sourcePosition);
} }
workoutOutgoingConnectionPoint(targetPosition: Point) { workoutOutgoingConnectionPoint(targetPosition: PositionType): PositionType {
$assert(targetPosition, 'targetPoint can not be null'); $assert(targetPosition, 'targetPoint can not be null');
const pos = this.getPosition(); const pos = this.getPosition();
const isAtRight = Shape.isAtRight(targetPosition, pos); const isAtRight = Shape.isAtRight(targetPosition, pos);
@ -148,7 +150,7 @@ class MainTopic extends Topic {
} else { } else {
result = Shape.calculateRectConnectionPoint(pos, size, isAtRight); result = Shape.calculateRectConnectionPoint(pos, size, isAtRight);
} }
return new Point(result.x, result.y); return { ...result };
} }
} }

View File

@ -24,10 +24,14 @@ import DragTopic from './DragTopic';
import LayoutManager from './layout/LayoutManager'; import LayoutManager from './layout/LayoutManager';
import SizeType from './SizeType'; import SizeType from './SizeType';
export type NodeOption = {
readOnly: boolean;
};
abstract class NodeGraph { abstract class NodeGraph {
private _mouseEvents: boolean; private _mouseEvents: boolean;
private _options; private _options: NodeOption;
private _onFocus: boolean; private _onFocus: boolean;
@ -37,7 +41,7 @@ abstract class NodeGraph {
private _elem2d: ElementClass; private _elem2d: ElementClass;
constructor(nodeModel: NodeModel, options) { constructor(nodeModel: NodeModel, options: NodeOption) {
$assert(nodeModel, 'model can not be null'); $assert(nodeModel, 'model can not be null');
this._options = options; this._options = options;

View File

@ -31,8 +31,8 @@ class ShirinkConnector {
const ellipse = new Elipse(TopicConfig.INNER_RECT_ATTRIBUTES); const ellipse = new Elipse(TopicConfig.INNER_RECT_ATTRIBUTES);
this._ellipse = ellipse; this._ellipse = ellipse;
const fillColor = topic.getConnectionColor(); const color = topic.getConnectionColor();
ellipse.setFill(fillColor); ellipse.setFill(color);
ellipse.setSize(TopicConfig.CONNECTOR_WIDTH, TopicConfig.CONNECTOR_WIDTH); ellipse.setSize(TopicConfig.CONNECTOR_WIDTH, TopicConfig.CONNECTOR_WIDTH);
ellipse.addEvent('click', (event: Event) => { ellipse.addEvent('click', (event: Event) => {

View File

@ -17,9 +17,9 @@
*/ */
import { $assert, $defined } from '@wisemapping/core-js'; import { $assert, $defined } from '@wisemapping/core-js';
import { Rect, Image, Line, Text, Group, ElementClass, Point } from '@wisemapping/web2d'; import { Rect, Line, Text, Group, ElementClass } from '@wisemapping/web2d';
import NodeGraph from './NodeGraph'; import NodeGraph, { NodeOption } from './NodeGraph';
import TopicConfig from './TopicConfig'; import TopicConfig from './TopicConfig';
import TopicStyle from './TopicStyle'; import TopicStyle from './TopicStyle';
import TopicFeatureFactory from './TopicFeature'; import TopicFeatureFactory from './TopicFeature';
@ -40,6 +40,9 @@ import LinkModel from './model/LinkModel';
import SizeType from './SizeType'; import SizeType from './SizeType';
import FeatureModel from './model/FeatureModel'; import FeatureModel from './model/FeatureModel';
import ImageIcon from './ImageIcon'; import ImageIcon from './ImageIcon';
import PositionType from './PositionType';
import LineTopicShape from './widget/LineTopicShape';
import ImageTopicShape from './widget/ImageTopicShape';
const ICON_SCALING_FACTOR = 1.3; const ICON_SCALING_FACTOR = 1.3;
@ -66,7 +69,7 @@ abstract class Topic extends NodeGraph {
private _outgoingLine: ConnectionLine | null; private _outgoingLine: ConnectionLine | null;
constructor(model: NodeModel, options) { constructor(model: NodeModel, options: NodeOption) {
super(model, options); super(model, options);
this._children = []; this._children = [];
this._parent = null; this._parent = null;
@ -179,12 +182,18 @@ abstract class Topic extends NodeGraph {
// Style is infered looking recursivelly on the parent nodes. // Style is infered looking recursivelly on the parent nodes.
if (!result) { if (!result) {
const parent = this.getParent(); const parent = this.getParent();
if (parent) { if (parent && parent.isCentralTopic()) {
result = parent.getConnectionColor(); // This means that this is central main node, in this case, I will overwrite with the main color if it was defined.
result = this.getModel().getConnectionColor() || parent.getModel().getConnectionColor();
} else { } else {
result = parent?.getConnectionColor();
}
}
if (!result) {
result = TopicStyle.defaultConnectionColor(this); result = TopicStyle.defaultConnectionColor(this);
} }
}
return result!; return result!;
} }
@ -219,74 +228,34 @@ abstract class Topic extends NodeGraph {
return this._innerShape; return this._innerShape;
} }
protected _buildShape(attributes, shapeType: TopicShapeType): ElementClass { protected _buildShape(attributes: object, shapeType: TopicShapeType): ElementClass {
$assert(attributes, 'attributes can not be null');
$assert(shapeType, 'shapeType can not be null');
let result: ElementClass; let result: ElementClass;
if (shapeType === 'rectangle') { switch (shapeType) {
case 'rectangle':
result = new Rect(0, attributes); result = new Rect(0, attributes);
} else if (shapeType === 'image') { break;
const model = this.getModel(); case 'elipse':
const url = model.getImageUrl();
const size = model.getImageSize();
result = new Image();
result.setHref(url);
result.setSize(size.width, size.height);
result.getSize = function getSize() {
return model.getImageSize();
};
result.setPosition = function setPosition() {
// Ignore ...
};
} else if (shapeType === 'elipse') {
result = new Rect(0.9, attributes); result = new Rect(0.9, attributes);
} else if (shapeType === 'rounded rectangle') { break;
case 'rounded rectangle':
result = new Rect(0.3, attributes); result = new Rect(0.3, attributes);
} else if (shapeType === 'line') { break;
const stokeColor = this.getConnectionColor(); case 'line':
result = new Line({ result = new LineTopicShape(this);
strokeColor: stokeColor, break;
strokeWidth: 1, case 'image':
}); result = new ImageTopicShape(this);
break;
const me = this; default: {
result.setSize = function setSize(width: number, height: number) { const exhaustiveCheck: never = shapeType;
this.size = { width, height }; throw new Error(exhaustiveCheck);
result.setFrom(0, height); }
result.setTo(width, height);
// // Lines will have the same color of the default connection lines...
const color = me.getConnectionColor();
result.setStroke(1, 'solid', color);
};
result.getSize = function getSize() {
return this.size;
};
result.setPosition = () => {
// Overwrite behaviour ...
};
result.setFill = () => {
// Overwrite behaviour ...
};
result.setStroke = () => {
// Overwrite behaviour ...
};
} else {
$assert(false, `Unsupported figure shapeType:${shapeType}`);
} }
result.setPosition(0, 0); result.setPosition(0, 0);
return result; return result;
} }
setCursor(type: string) { setCursor(type: string): void {
const innerShape = this.getInnerShape(); const innerShape = this.getInnerShape();
innerShape.setCursor(type); innerShape.setCursor(type);
@ -583,6 +552,9 @@ abstract class Topic extends NodeGraph {
const model = this.getModel(); const model = this.getModel();
model.setConnectionColor(value); model.setConnectionColor(value);
// Force redraw for changing line color ...
this.redraw();
// Needs to change change all the lines color. Outgoing are part of the children. // Needs to change change all the lines color. Outgoing are part of the children.
this.getChildren().forEach((topic: Topic) => topic.redraw()); this.getChildren().forEach((topic: Topic) => topic.redraw());
@ -738,7 +710,7 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
setChildrenShrunken(value: boolean) { setChildrenShrunken(value: boolean): void {
// Update Model ... // Update Model ...
const model = this.getModel(); const model = this.getModel();
model.setChildrenShrunken(value); model.setChildrenShrunken(value);
@ -860,7 +832,7 @@ abstract class Topic extends NodeGraph {
/** /**
* Point: references the center of the rect shape.!!! * Point: references the center of the rect shape.!!!
*/ */
setPosition(point: Point) { setPosition(point: PositionType): void {
$assert(point, 'position can not be null'); $assert(point, 'position can not be null');
// allowed param reassign to avoid risks of existing code relying in this side-effect // allowed param reassign to avoid risks of existing code relying in this side-effect
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@ -1306,6 +1278,7 @@ abstract class Topic extends NodeGraph {
this._outgoingLine.redraw(); this._outgoingLine.redraw();
this._connector.setFill(color); this._connector.setFill(color);
this.getChildren().forEach((t) => t.redraw()); this.getChildren().forEach((t) => t.redraw());
result = true;
} }
} }
} }
@ -1341,6 +1314,17 @@ abstract class Topic extends NodeGraph {
const yPosition = Math.round((topicHeight - textHeight) / 2); const yPosition = Math.round((topicHeight - textHeight) / 2);
iconGroup.setPosition(padding, yPosition); iconGroup.setPosition(padding, yPosition);
textShape.setPosition(padding + iconGroupWith + textIconSpacing, yPosition); textShape.setPosition(padding + iconGroupWith + textIconSpacing, yPosition);
// Has color changed ?
if (this.getShapeType() === 'line') {
const color = this.getConnectionColor();
this.getInnerShape().setStroke(1, 'solid', color);
// Force the repaint in case that the main topic color has changed.
if (this.getParent() && this.getParent()?.isCentralTopic()) {
this._outgoingLine?.redraw();
}
}
} else { } else {
// In case of images, the size is fixed ... // In case of images, the size is fixed ...
const size = this.getModel().getImageSize(); const size = this.getModel().getImageSize();
@ -1367,9 +1351,9 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
abstract workoutOutgoingConnectionPoint(position: Point): Point; abstract workoutOutgoingConnectionPoint(position: PositionType): PositionType;
abstract workoutIncomingConnectionPoint(position: Point): Point; abstract workoutIncomingConnectionPoint(position: PositionType): PositionType;
isChildTopic(childTopic: Topic): boolean { isChildTopic(childTopic: Topic): boolean {
let result = this.getId() === childTopic.getId(); let result = this.getId() === childTopic.getId();

View File

@ -3,10 +3,11 @@ import { $assert } from '@wisemapping/core-js';
import CentralTopic from './CentralTopic'; import CentralTopic from './CentralTopic';
import MainTopic from './MainTopic'; import MainTopic from './MainTopic';
import NodeModel from './model/NodeModel'; import NodeModel from './model/NodeModel';
import { NodeOption } from './NodeGraph';
import Topic from './Topic'; import Topic from './Topic';
class TopicFactory { class TopicFactory {
static create(nodeModel: NodeModel, options: object): Topic { static create(nodeModel: NodeModel, options: NodeOption): Topic {
$assert(nodeModel, 'Model can not be null'); $assert(nodeModel, 'Model can not be null');
const type = nodeModel.getType(); const type = nodeModel.getType();

View File

@ -178,16 +178,6 @@ class NodeModel extends INodeModel {
if (backgroundColor) { if (backgroundColor) {
this.setBackgroundColor(backgroundColor); this.setBackgroundColor(backgroundColor);
} }
const connectType = value.getConnectionStyle();
if ($defined(connectType)) {
this.setConnectionStyle(connectType!);
}
const connectColor = value.getConnectionColor();
if ($defined(connectColor)) {
this.setConnectionColor(connectColor!);
}
} }
deepCopy(): NodeModel { deepCopy(): NodeModel {

View File

@ -0,0 +1,45 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Image } from '@wisemapping/web2d';
import SizeType from '../SizeType';
import Topic from '../Topic';
class ImageTopicShape extends Image {
private _topic: Topic;
constructor(topic: Topic) {
super();
const model = topic.getModel();
const url = model.getImageUrl();
const size = model.getImageSize();
super.setHref(url);
super.setSize(size.width, size.height);
this._topic = topic;
}
getSize(): SizeType {
return this._topic.getModel().getImageSize();
}
setPosition(): void {
// Ignore ...
}
}
export default ImageTopicShape;

View File

@ -0,0 +1,55 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Line } from '@wisemapping/web2d';
import SizeType from '../SizeType';
import Topic from '../Topic';
class LineTopicShape extends Line {
private _topic: Topic;
private _size: SizeType;
constructor(topic: Topic) {
const stokeColor = topic.getConnectionColor();
super({
strokeColor: stokeColor,
strokeWidth: 1,
});
this._topic = topic;
}
setSize(width: number, height: number): void {
this._size = { width, height };
super.setFrom(0, height);
super.setTo(width, height);
}
getSize() {
return this._size;
}
setPosition() {
// Overwrite behaviour ...
}
setFill() {
// Overwrite behaviour ...
}
}
export default LineTopicShape;