Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port Image Edit Operations #2670

Merged
merged 62 commits into from
Jun 5, 2024
Merged

Conversation

juliaroldi
Copy link
Contributor

@juliaroldi juliaroldi commented May 29, 2024

This change aims to Port Image edit operations from Image Edit plugin to Content Model Plugin. This feature is responsible for resize, rotate, crop, flip, resize by percentage and rotate by degree. This also removes the old Image Edit Plugin from demo site. The changes related to edit images with mouse selection will be done in a second PR.

Progress:

Feature Status
Resize handles Done
Rotate handle Done
Crop handles Done
Flip Done
Resize by percentage Done
Rotate by degree Done
Unit test Done

@juliaroldi juliaroldi marked this pull request as ready for review May 30, 2024 13:58

/**
* Start to crop selected image
*/
cropImage(): void;
cropImage(image: HTMLImageElement): void;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of passing in the image, can we just use current selected image?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, but I was following the pattern of ImageEditor interface

/**
* Resize and rotate an image
*/
| 'resizeAndRotate';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used when we right click an image and add the wrapper, can be removed for now.

@@ -56,7 +55,9 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC
core.api.setEditorStyle(
core,
DOM_SELECTION_CSS_KEY,
`outline-style:auto!important; outline-color:${imageSelectionColor}!important;`,
`outline-style:auto!important; outline-color:${imageSelectionColor}!important;display: ${
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems #2676 is caused by the outline-style here. Can we just fix it as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

@@ -56,7 +55,9 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC
core.api.setEditorStyle(
core,
DOM_SELECTION_CSS_KEY,
`outline-style:auto!important; outline-color:${imageSelectionColor}!important;`,
`outline-style:auto!important; outline-color:${imageSelectionColor}!important;display: ${
core.environment.isSafari ? 'inline-block' : 'inline-flex'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious, do we still need this check today?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need. :(

entryPoint?: string
): HTMLImageElement {
const parent = image.parentElement;
// console.log(parent, entryPoint);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this

/**
* @internal
*/
export function getContentModelImage(editor: IEditor): ContentModelImage | null {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this used? This will give you a cloned object but not the original image object. Is that ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used to get the image dataset

* @internal
* Definition of ImageMetadataFormat
*/
export const ImageMetadataFormatDefinition = createObjectDefinition<Required<ImageMetadataFormat>>({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this used externally?

}
}

public rotateImage(image: HTMLImageElement, angleRad: number) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comment for public methods

) {
editor.formatContentModel(
(model, _) => {
const selectedSegmentsAndParagraphs = getSelectedSegmentsAndParagraphs(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here we edit image based on current selection, then why we need to pass in image object in other editing functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We pass the image element to add and remove the wrapper element. Here we edit the selected content model image

}

public canRegenerateImage(image: HTMLImageElement): boolean {
return canRegenerateImage(image) || canRegenerateImage(this.selectedImage);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be very confusing. It checks either the passed in image or current selection. We should make the function behavior predictable, only check one image

const selectedSegments = getSelectedSegments(model, false /*includeFormatHolder*/);
if (selectedSegments.length == 1 && selectedSegments[0].segmentType == 'Image') {
return selectedSegments[0];
return selectedSegments[0] as ContentModelImage;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine here, but if we do this kind of type cast in other place, it will be dangerous.

Actually, if you only want dataset, just get it directly from the connect model, and return the dataset instead.

Disconnected model actually need a clone, which is not as good perf as connected one.

function getSelectedImageDataset() {
  let result;

  editor.formatContentModel(model => {
    const image = getSelectedImage(model);
    result = getImageMetadata(image);
    return false;
  });

  return result;

Something like this.

const paragraph = selectedSegmentsAndParagraphs[0][1];

if (paragraph && segment.segmentType == 'Image') {
mutateSegment(paragraph, segment, image => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice.

And I think we only need to return true for formatContentModel when image is really changed, right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually you can move this down into the "if" block below.

`outline-style:auto!important; outline-color:${imageSelectionColor}!important;display: ${
core.environment.isSafari ? 'inline-block' : 'inline-flex'
`outline-style:solid!important; outline-color:${imageSelectionColor}!important;display: ${
core.environment.isSafari ? '-webkit-inline-flex' : 'inline-flex'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!

@juliaroldi juliaroldi merged commit b4fc6d6 into master Jun 5, 2024
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants