diff options
author | Tom Schindl | 2016-03-10 21:16:21 +0000 |
---|---|---|
committer | Tom Schindl | 2016-03-10 21:16:21 +0000 |
commit | 56381a61f80547963ca4062df7586b1dc7da9c82 (patch) | |
tree | c8abccfd66b38d81aae10a7a4ed5630dce1e235c | |
parent | 161e137b03d6fc0fa351e94208528e3cecd65f94 (diff) | |
parent | 5ef4a6745e2107eede6e789a625cc108eec53607 (diff) | |
download | org.eclipse.efxclipse-56381a61f80547963ca4062df7586b1dc7da9c82.tar.gz org.eclipse.efxclipse-56381a61f80547963ca4062df7586b1dc7da9c82.tar.xz org.eclipse.efxclipse-56381a61f80547963ca4062df7586b1dc7da9c82.zip |
Merge branch 'master' of ssh://tschindl@git.eclipse.org:29418/efxclipse/org.eclipse.efxclipse
43 files changed, 1261 insertions, 137 deletions
diff --git a/bundles/code/org.eclipse.fx.code.editor.fx.themes/css/dark-highlight.css b/bundles/code/org.eclipse.fx.code.editor.fx.themes/css/dark-highlight.css index 9ad8aa231..4e0fc4837 100644 --- a/bundles/code/org.eclipse.fx.code.editor.fx.themes/css/dark-highlight.css +++ b/bundles/code/org.eclipse.fx.code.editor.fx.themes/css/dark-highlight.css @@ -46,13 +46,46 @@ -fx-stroke: #c7c7c7; } +.styled-text-hover .errors { + -fx-background-color: darkred; + -fx-padding: 10; + -fx-background-radius: 5; +} +.styled-text-hover .warnings { + -fx-background-color: darkorange; + -fx-padding: 10; + -fx-background-radius: 5; +} +.styled-text-hover .infos { + -fx-background-color: blanchediamond; + -fx-padding: 10; + -fx-background-radius: 5; +} +.styled-text-hover .docs { + -fx-background-color: darkblue; + -fx-padding: 10; + -fx-background-radius: 5; +} +.styled-text-hover .others { + -fx-background-color: gray; + -fx-padding: 10; + -fx-background-radius: 5; +} + +.styled-text-hover .context-info { + -fx-background-color: #393939; + -fx-border-color: black; + -fx-border-width: thin; +} + .styled-text-hover-text { -fx-text-fill: #b8c4d1; } .styled-text-hover { - -fx-background-color: #2b5696; +/* -fx-background-color: #2b5696; -fx-background-radius: 5; -fx-padding: 10; -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.5) , 10, 0.0 , 0 , 3 ); +*/ }
\ No newline at end of file diff --git a/bundles/code/org.eclipse.fx.code.editor.fx.themes/css/highlight.css b/bundles/code/org.eclipse.fx.code.editor.fx.themes/css/highlight.css index 05673d98a..b25c6418e 100644 --- a/bundles/code/org.eclipse.fx.code.editor.fx.themes/css/highlight.css +++ b/bundles/code/org.eclipse.fx.code.editor.fx.themes/css/highlight.css @@ -23,9 +23,42 @@ -source-editor-markup-extra: rgb(128, 128, 128); } +.styled-text-hover .errors { + -fx-background-color: red; + -fx-padding: 10; + -fx-background-radius: 5; +} +.styled-text-hover .warnings { + -fx-background-color: darkorange; + -fx-padding: 10; + -fx-background-radius: 5; +} +.styled-text-hover .infos { + -fx-background-color: blanchediamond; + -fx-padding: 10; + -fx-background-radius: 5; +} +.styled-text-hover .docs { + -fx-background-color: aliceblue; + -fx-padding: 10; + -fx-background-radius: 5; +} +.styled-text-hover .others { + -fx-background-color: gray; + -fx-padding: 10; + -fx-background-radius: 5; +} + +.styled-text-hover .context-info { + -fx-background-color: antiquewhite; + -fx-border-color: black; + -fx-border-width: thin; +} + .styled-text-hover { - -fx-background-color: #ecebec; +/* -fx-background-color: #ecebec; -fx-background-radius: 5; -fx-padding: 10; -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.5) , 10, 0.0 , 0 , 3 ); +*/ }
\ No newline at end of file diff --git a/bundles/code/org.eclipse.fx.code.editor.fx/META-INF/MANIFEST.MF b/bundles/code/org.eclipse.fx.code.editor.fx/META-INF/MANIFEST.MF index b5ae15982..ea1facf96 100644 --- a/bundles/code/org.eclipse.fx.code.editor.fx/META-INF/MANIFEST.MF +++ b/bundles/code/org.eclipse.fx.code.editor.fx/META-INF/MANIFEST.MF @@ -11,6 +11,7 @@ Import-Package: javax.inject, org.eclipse.fx.core;version="2.3.0", org.eclipse.fx.core.di;version="2.3.0", org.eclipse.fx.core.preferences;version="2.3.0", + org.eclipse.fx.text.hover, org.eclipse.fx.text.rules;version="2.3.0", org.eclipse.fx.text.ui;version="2.3.0", org.eclipse.fx.text.ui.contentassist;version="2.3.0", diff --git a/bundles/code/org.eclipse.fx.code.editor.fx/src/org/eclipse/fx/code/editor/fx/services/CompletionProposalPresenter.java b/bundles/code/org.eclipse.fx.code.editor.fx/src/org/eclipse/fx/code/editor/fx/services/CompletionProposalPresenter.java index 94741605a..aa42c39b0 100644 --- a/bundles/code/org.eclipse.fx.code.editor.fx/src/org/eclipse/fx/code/editor/fx/services/CompletionProposalPresenter.java +++ b/bundles/code/org.eclipse.fx.code.editor.fx/src/org/eclipse/fx/code/editor/fx/services/CompletionProposalPresenter.java @@ -6,4 +6,8 @@ import org.eclipse.fx.text.ui.contentassist.ICompletionProposal; @SuppressWarnings("restriction") public interface CompletionProposalPresenter { public ICompletionProposal createProposal(CompletionProposal proposal); + + public default String getAutoTriggers() { + return ""; + } } diff --git a/bundles/code/org.eclipse.fx.code.editor.fx/src/org/eclipse/fx/code/editor/fx/services/internal/DefaultSourceViewerConfiguration.java b/bundles/code/org.eclipse.fx.code.editor.fx/src/org/eclipse/fx/code/editor/fx/services/internal/DefaultSourceViewerConfiguration.java index 3feefb7c1..51240797f 100644 --- a/bundles/code/org.eclipse.fx.code.editor.fx/src/org/eclipse/fx/code/editor/fx/services/internal/DefaultSourceViewerConfiguration.java +++ b/bundles/code/org.eclipse.fx.code.editor.fx/src/org/eclipse/fx/code/editor/fx/services/internal/DefaultSourceViewerConfiguration.java @@ -17,6 +17,7 @@ import org.eclipse.fx.code.editor.services.HoverInformationProvider; import org.eclipse.fx.code.editor.services.ProposalComputer; import org.eclipse.fx.code.editor.services.ProposalComputer.ProposalContext; import org.eclipse.fx.core.preferences.Preference; +import org.eclipse.fx.text.hover.HoverInfo; import org.eclipse.fx.text.ui.Feature; import org.eclipse.fx.text.ui.ITextHover; import org.eclipse.fx.text.ui.ITextViewer; @@ -33,6 +34,7 @@ import org.eclipse.fx.text.ui.source.SourceViewerConfiguration; import org.eclipse.fx.ui.controls.styledtext.TextSelection; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import javafx.beans.property.SetProperty; @@ -135,6 +137,25 @@ public class DefaultSourceViewerConfiguration extends SourceViewerConfiguration } @Override + public Set<HoverInfo> getDocumentHoverInfo(IDocument document, int offset) { + if (hoverInformationProvider != null) { + return hoverInformationProvider.getDocumentHoverProvider().getHoverInfo(document, offset); + } + return super.getDocumentHoverInfo(document, offset); + } + + @Override + public Set<HoverInfo> getAnnotationHoverInfo(Annotation annotation) { + if (hoverInformationProvider != null) { + return hoverInformationProvider.getAnnotationHoverProviders().stream() + .filter(p->p.isApplicable(annotation.getClass())) + .map(p->p.getHoverInfo(annotation)) + .collect(Collectors.toSet()); + } + return super.getAnnotationHoverInfo(annotation); + } + + @Override public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { if( hoverInformationProvider != null ) { return new ITextHover() { @@ -213,4 +234,9 @@ public class DefaultSourceViewerConfiguration extends SourceViewerConfiguration return null; } } + + @Override + public String getContentAssistAutoTriggers() { + return proposalPresenter.getAutoTriggers(); + } } diff --git a/bundles/code/org.eclipse.fx.code.editor/META-INF/MANIFEST.MF b/bundles/code/org.eclipse.fx.code.editor/META-INF/MANIFEST.MF index e01ccb14d..5ee6d1018 100644 --- a/bundles/code/org.eclipse.fx.code.editor/META-INF/MANIFEST.MF +++ b/bundles/code/org.eclipse.fx.code.editor/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Import-Package: javax.inject, org.eclipse.fx.core.adapter;version="2.3.0", org.eclipse.fx.core.event;version="2.3.0", org.eclipse.fx.core.function;version="2.3.0", + org.eclipse.fx.text.hover, org.eclipse.jface.text.rules, org.osgi.service.component.annotations;version="1.2.0" Require-Bundle: org.eclipse.text;bundle-version="3.5.400", diff --git a/bundles/code/org.eclipse.fx.code.editor/src/org/eclipse/fx/code/editor/services/HoverInformationProvider.java b/bundles/code/org.eclipse.fx.code.editor/src/org/eclipse/fx/code/editor/services/HoverInformationProvider.java index 87ec6cc8f..a83d35abf 100644 --- a/bundles/code/org.eclipse.fx.code.editor/src/org/eclipse/fx/code/editor/services/HoverInformationProvider.java +++ b/bundles/code/org.eclipse.fx.code.editor/src/org/eclipse/fx/code/editor/services/HoverInformationProvider.java @@ -1,8 +1,18 @@ package org.eclipse.fx.code.editor.services; +import java.util.Set; + +import org.eclipse.fx.text.hover.AnnotationHoverProvider; +import org.eclipse.fx.text.hover.DocumentHoverProvider; +import org.eclipse.fx.text.hover.HoverInfo; +import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; public interface HoverInformationProvider { public CharSequence getHoverInformation(String partitionType, IRegion region); public IRegion getHoverRegion(String partitionType, int offset); + + DocumentHoverProvider getDocumentHoverProvider(); + Set<AnnotationHoverProvider> getAnnotationHoverProviders(); + } diff --git a/bundles/code/org.eclipse.fx.text.ui/META-INF/MANIFEST.MF b/bundles/code/org.eclipse.fx.text.ui/META-INF/MANIFEST.MF index 3778e4a18..e4b5b683c 100644 --- a/bundles/code/org.eclipse.fx.text.ui/META-INF/MANIFEST.MF +++ b/bundles/code/org.eclipse.fx.text.ui/META-INF/MANIFEST.MF @@ -18,6 +18,7 @@ Export-Package: org.eclipse.fx.text.ui;version="2.3.0";x-internal:=true, Bundle-Vendor: Eclipse.org Import-Package: com.google.common.collect;version="15.0.0", org.eclipse.fx.core;version="2.3.0", + org.eclipse.fx.text.hover, org.eclipse.fx.ui.controls.list;version="2.3.0", org.eclipse.fx.ui.controls.styledtext;version="2.3.0", org.eclipse.fx.ui.controls.styledtext.events;version="2.3.0", diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/TextViewer.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/TextViewer.java index d3d56b1d0..156eefe85 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/TextViewer.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/TextViewer.java @@ -14,15 +14,18 @@ package org.eclipse.fx.text.ui; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.fx.core.Subscription; import org.eclipse.fx.core.Util; +import org.eclipse.fx.text.hover.HoverInfo; import org.eclipse.fx.text.ui.internal.InvisibleCharSupport; import org.eclipse.fx.text.ui.internal.LineNumberSupport; import org.eclipse.fx.ui.controls.styledtext.StyleRange; @@ -49,6 +52,7 @@ import org.eclipse.jface.text.SlaveDocumentEvent; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.projection.ChildDocument; import org.eclipse.jface.text.projection.ChildDocumentManager; +import org.eclipse.jface.text.source.Annotation; import javafx.beans.property.SetProperty; import javafx.beans.property.SimpleSetProperty; @@ -151,6 +155,14 @@ public class TextViewer extends AnchorPane implements ITextViewer, ITextViewerEx return this.fUndoManager; } + public Set<HoverInfo> getHoverInfo(int offset) { + return Collections.emptySet(); + } + + public Set<HoverInfo> getHoverInfo(Annotation annotation) { + return Collections.emptySet(); + } + /** * Create the text control */ @@ -183,7 +195,7 @@ public class TextViewer extends AnchorPane implements ITextViewer, ITextViewerEx return kc; } - private void onVerify(VerifyEvent event) { + protected void onVerify(VerifyEvent event) { // if (event.isControlDown() && getKeyCode(event) == KeyCode.L) { // if (getFeatures().contains(Feature.SHOW_LINE_NUMBERS)) { @@ -192,7 +204,7 @@ public class TextViewer extends AnchorPane implements ITextViewer, ITextViewerEx // else { // getFeatures().add(Feature.SHOW_LINE_NUMBERS); // } -// System.err.println(getFeatures().get()); +// System.err.println("SETTING FEATURES: " + getFeatures().get()); // } // // if (event.isControlDown() && getKeyCode(event) == KeyCode.K) { @@ -202,7 +214,7 @@ public class TextViewer extends AnchorPane implements ITextViewer, ITextViewerEx // else { // getFeatures().add(Feature.SHOW_HIDDEN_SYMBOLS); // } -// System.err.println(getFeatures().get()); +// System.err.println("SETTING FEATURES: " + getFeatures().get()); // } diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/TextViewerHoverManager.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/TextViewerHoverManager.java index eb0df6c1c..f89418916 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/TextViewerHoverManager.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/TextViewerHoverManager.java @@ -10,20 +10,50 @@ *******************************************************************************/ package org.eclipse.fx.text.ui; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.fx.text.hover.DocumentHoverProvider; +import org.eclipse.fx.text.hover.HoverInfo; +import org.eclipse.fx.text.ui.hover.HoverPresenter; +import org.eclipse.fx.text.ui.hover.HoverWindowPresenter; +import org.eclipse.fx.text.ui.hover.internal.DefaultHoverPresenter; +import org.eclipse.fx.text.ui.hover.internal.DefaultHoverWindowPresenter; import org.eclipse.fx.ui.controls.styledtext.StyledTextArea; +import org.eclipse.fx.ui.controls.styledtext.events.HoverTarget; import org.eclipse.fx.ui.controls.styledtext.events.TextHoverEvent; +import org.eclipse.fx.ui.controls.styledtext.model.Annotation; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; +import javafx.geometry.Bounds; import javafx.geometry.Point2D; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.stage.PopupWindow; public class TextViewerHoverManager { + + List<DocumentHoverProvider> providers = new ArrayList<>(); + + DefaultHoverWindowPresenter windowPresenter; + List<HoverPresenter> hoverPresenters = new ArrayList<>(); + private final TextViewer textViewer; private final PopupWindow popup; private final BorderPane root; public TextViewerHoverManager(TextViewer textViewer) { + + this.windowPresenter = new DefaultHoverWindowPresenter(textViewer.getTextWidget()); + this.hoverPresenters.add(new DefaultHoverPresenter()); + this.windowPresenter.setHoverPresenter(this.hoverPresenters); + this.textViewer = textViewer; this.popup = new PopupWindow() { }; @@ -52,31 +82,115 @@ public class TextViewerHoverManager { return root; } + + public void install(StyledTextArea styledTextArea) { styledTextArea.addEventHandler(TextHoverEvent.HOVER, e -> { + System.err.println(e.getOffset() + " " + e); if( e.getOffset() > 0 ) { - final ITextHover hover= getTextViewer().getTextHover(e.getOffset(), /*getHoverEventStateMask()*/ ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); - if( hover != null ) { - String text = hover.getHoverInfo(getTextViewer(), hover.getHoverRegion(getTextViewer(), e.getOffset())); - if( text != null && ! text.isEmpty() ) { - Label value = new Label(text); - value.getStyleClass().add("styled-text-hover-text"); - getRoot().setCenter(value); - Point2D locationAtOffset = getTextViewer().getTextWidget().getLocationAtOffset(e.getOffsetTokenStart()); - double x = e.getScreenX(); - if( locationAtOffset != null ) { - x = getTextViewer().getTextWidget().localToScreen(locationAtOffset.getX(),0).getX(); - } - getPopup().show(getTextViewer().getTextWidget().getScene().getWindow(), x, e.getScreenY()+5); - } else { - getPopup().hide(); + final IDocument document = getTextViewer().getDocument(); + final int offset = e.getOffset(); + + + List<HoverInfo> hovers = new ArrayList<>(); + + hovers.addAll(getTextViewer().getHoverInfo(e.getOffset())); + + hovers.addAll(this.providers.stream().flatMap(p->p.getHoverInfo(document, offset).stream()).collect(Collectors.toSet())); + + Set<HoverTarget> annotationTargets = e.getHoverTargets().stream() + .filter(t->t.model instanceof Annotation) + .collect(Collectors.toSet()); + + Set<HoverInfo> annotationHovers = e.getHoverTargets().stream() + .filter(t->t.model instanceof Annotation) + .map(t->(Annotation) t.model) + .filter(a->a.getModel() instanceof org.eclipse.jface.text.source.Annotation) + .map(a->(org.eclipse.jface.text.source.Annotation)a.getModel()) + .flatMap(a->getTextViewer().getHoverInfo(a).stream()) + .collect(Collectors.toSet()); + + System.err.println("AnnotationHovers: " + annotationHovers); + hovers.addAll(annotationHovers); + + + +// if( e.getOffset() > 0 ) { +// final ITextHover hover= getTextViewer().getTextHover(e.getOffset(), /*getHoverEventStateMask()*/ ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); +// if( hover != null ) { +// String text = hover.getHoverInfo(getTextViewer(), hover.getHoverRegion(getTextViewer(), e.getOffset())); +// if( text != null && ! text.isEmpty() ) { +// Label value = new Label(text); +// value.getStyleClass().add("styled-text-hover-text"); +// getRoot().setCenter(value); +// Point2D locationAtOffset = getTextViewer().getTextWidget().getLocationAtOffset(e.getOffsetTokenStart()); +// double x = e.getScreenX(); +// if( locationAtOffset != null ) { +// x = getTextViewer().getTextWidget().localToScreen(locationAtOffset.getX(),0).getX(); +// } +// getPopup().show(getTextViewer().getTextWidget().getScene().getWindow(), x, e.getScreenY()+5); +// } else { +// getPopup().hide(); +// } +// } else { +// getPopup().hide(); +// } +// } else { +// getPopup().hide(); +// } + + System.err.println("found hover infos: " + hovers); + + + + + if (!hovers.isEmpty()) { + // TODO on multiple hovers we need to determine which screenAnchor to use°!! + Point2D anchor = e.getHoverTargets().get(0).screenAnchor; + Bounds bounds = e.getHoverTargets().get(0).screenBounds; + if (!annotationHovers.isEmpty()) { + HoverTarget next = annotationTargets.iterator().next(); + anchor = next.screenAnchor; + bounds = next.screenBounds; } - } else { - getPopup().hide(); + + System.err.println("showing @ " + anchor); + + + this.windowPresenter.show(anchor, bounds, hovers); + } + else { + this.windowPresenter.hide(); } - } else { - getPopup().hide(); } + else { + this.windowPresenter.hide(); + } + + +// if( e.getOffset() > 0 ) { +// final ITextHover hover= getTextViewer().getTextHover(e.getOffset(), /*getHoverEventStateMask()*/ ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK); +// if( hover != null ) { +// String text = hover.getHoverInfo(getTextViewer(), hover.getHoverRegion(getTextViewer(), e.getOffset())); +// if( text != null && ! text.isEmpty() ) { +// Label value = new Label(text); +// value.getStyleClass().add("styled-text-hover-text"); +// getRoot().setCenter(value); +// Point2D locationAtOffset = getTextViewer().getTextWidget().getLocationAtOffset(e.getOffsetTokenStart()); +// double x = e.getScreenX(); +// if( locationAtOffset != null ) { +// x = getTextViewer().getTextWidget().localToScreen(locationAtOffset.getX(),0).getX(); +// } +// getPopup().show(getTextViewer().getTextWidget().getScene().getWindow(), x, e.getScreenY()+5); +// } else { +// getPopup().hide(); +// } +// } else { +// getPopup().hide(); +// } +// } else { +// getPopup().hide(); +// } }); } } diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContentAssistant.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContentAssistant.java index 046158db3..6c2fdf1c5 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContentAssistant.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContentAssistant.java @@ -11,13 +11,19 @@ package org.eclipse.fx.text.ui.contentassist; import java.util.List; +import java.util.Optional; import java.util.function.Function; -import javafx.geometry.Point2D; -import javafx.scene.input.KeyCode; - import org.eclipse.fx.text.ui.ITextViewer; +import org.eclipse.fx.ui.controls.styledtext.StyledTextArea.LineLocation; import org.eclipse.fx.ui.controls.styledtext.VerifyEvent; +import org.eclipse.jface.text.IDocument; + +import javafx.animation.Timeline; +import javafx.application.Platform; +import javafx.geometry.Point2D; +import javafx.scene.input.KeyCode; +import javafx.util.Duration; public class ContentAssistant implements IContentAssistant { private final Function<ContentAssistContextData, List<ICompletionProposal>> proposalComputer; @@ -25,6 +31,11 @@ public class ContentAssistant implements IContentAssistant { private ContentProposalPopup fProposalPopup; private ContextInformationPopup fContextInfoPopup; + private String autoTriggers = null; + public void setAutoTriggers(String autoTriggers) { + this.autoTriggers = autoTriggers; + } + public ContentAssistant(Function<ContentAssistContextData, List<ICompletionProposal>> proposalComputer) { this.proposalComputer = proposalComputer; } @@ -33,7 +44,8 @@ public class ContentAssistant implements IContentAssistant { public void install(ITextViewer textViewer) { if( this.fViewer == null ) { this.fViewer = textViewer; - this.fProposalPopup = new ContentProposalPopup(this, textViewer,proposalComputer); + this.fProposalPopup = new ContentProposalPopup(this, textViewer, proposalComputer); + textViewer.getTextWidget().addEventHandler(VerifyEvent.VERIFY, this::handleVerify); fContextInfoPopup = new ContextInformationPopup(this, textViewer); @@ -41,31 +53,67 @@ public class ContentAssistant implements IContentAssistant { } private void handleVerify(VerifyEvent event) { - if( !(event.isControlDown() && event.getCode() == KeyCode.SPACE) ) { + + boolean autoTrigger = !event.getText().isEmpty() && autoTriggers != null && autoTriggers.contains(event.getText()); + boolean defaultTrigger = event.isControlDown() && event.getCode() == KeyCode.SPACE; + + if( !(autoTrigger || defaultTrigger) ) { return; } - event.consume(); - final int offset = this.fViewer.getTextWidget().getCaretOffset(); + if (defaultTrigger) { + // we cannot consume auto triggers, since they need to be inserted into the document + event.consume(); + } + + // the proposal needs to be delayed until the char was typed + Platform.runLater(()-> { - List<ICompletionProposal> proposals = proposalComputer.apply(new ContentAssistContextData(offset, this.fViewer.getDocument()/*,""*/)); + final int offset = this.fViewer.getTextWidget().getCaretOffset(); - if( proposals.size() == 1) { - ICompletionProposal completionProposal = proposals.get(0); + System.err.println("caret offset = " + offset); - completionProposal.apply(this.fViewer.getDocument()); + List<ICompletionProposal> proposals = proposalComputer.apply(new ContentAssistContextData(offset, this.fViewer.getDocument()/*,""*/)); - showContextInformation(completionProposal.getContextInformation(), offset); - //this.fViewer.getTextWidget().setSelection(proposals.get(0).getSelection(this.fViewer.getDocument())); - } else if( ! proposals.isEmpty() ) { -// System.err.println(this.viewer.getTextWidget().getCaretLocation()); - System.err.println(); - Point2D p = this.fViewer.getTextWidget().getLocationAtOffset(this.fViewer.getTextWidget().getCaretOffset()); - System.err.println(p); - this.fProposalPopup.displayProposals(proposals, this.fViewer.getTextWidget().getCaretOffset(), this.fViewer.getTextWidget().localToScreen(p)); - } + if( proposals.size() == 1) { + ICompletionProposal completionProposal = proposals.get(0); + + completionProposal.apply(this.fViewer.getDocument()); + + showContextInformation(completionProposal.getContextInformation(), offset); + + //this.fViewer.getTextWidget().setSelection(proposals.get(0).getSelection(this.fViewer.getDocument())); + } else if( ! proposals.isEmpty() ) { + // System.err.println(this.viewer.getTextWidget().getCaretLocation()); + System.err.println(); + + Point2D p = this.fViewer.getTextWidget().getLocationAtOffset(this.fViewer.getTextWidget().getCaretOffset(), LineLocation.BELOW); + System.err.println(p); + + Point2D coords = this.fViewer.getTextWidget().localToScreen(p); + System.err.println(coords); + + Optional<ICompletionProposal> chosenProposal = this.fProposalPopup.displayProposals(proposals, this.fViewer.getTextWidget().getCaretOffset(), coords); + System.err.println("Chosen: " + chosenProposal.map(c->c.getLabel())); + + chosenProposal.ifPresent(proposal->{ + IDocument document = this.fViewer.getDocument(); + // apply the proposal + proposal.apply(document); + this.setAutoTriggers(autoTriggers); + this.fViewer.getTextWidget().setSelection(proposal.getSelection(document)); + + if (proposal.getContextInformation() != null) { + showContextInformation(proposal.getContextInformation(), fViewer.getTextWidget().getCaretOffset()); + } + + }); + } + + fViewer.getTextWidget().layout(); + }); } diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContentProposalPopup.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContentProposalPopup.java index b2409f4b5..e7c687374 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContentProposalPopup.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContentProposalPopup.java @@ -12,6 +12,8 @@ package org.eclipse.fx.text.ui.contentassist; import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import org.eclipse.fx.text.ui.ITextViewer; @@ -22,6 +24,8 @@ import org.eclipse.fx.ui.controls.styledtext.TextChangingEvent; import org.eclipse.fx.ui.controls.styledtext.VerifyEvent; import org.eclipse.jface.text.IDocument; +import com.sun.javafx.tk.Toolkit; + import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.value.ChangeListener; @@ -35,6 +39,7 @@ import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.web.WebView; import javafx.stage.PopupWindow; +import javafx.stage.Stage; public class ContentProposalPopup implements IContentAssistListener { ITextViewer viewer; @@ -47,7 +52,9 @@ public class ContentProposalPopup implements IContentAssistListener { private ContentAssistant fContentAssistant; private ChangeListener<Number> selectionChange; - private boolean proposalApplyInProgress; + + + private ICompletionProposal chosenProposal = null; public ContentProposalPopup(ContentAssistant assistant, ITextViewer viewer, Function<ContentAssistContextData, List<ICompletionProposal>> proposalComputer) { this.viewer = viewer; @@ -56,7 +63,8 @@ public class ContentProposalPopup implements IContentAssistListener { this.selectionChange = this::onSelectionChange; } - public void displayProposals(List<ICompletionProposal> proposalList, int offset, Point2D position) { + public Optional<ICompletionProposal> displayProposals(List<ICompletionProposal> proposalList, int offset, Point2D position) { + setup(); this.prefix = ""; //$NON-NLS-1$ this.offset = offset; @@ -72,6 +80,17 @@ public class ContentProposalPopup implements IContentAssistListener { this.stage.setOnHidden(this::unsubscribe); this.stage.show(this.viewer.getTextWidget().getScene().getWindow()); + + + chosenProposal = null; + Toolkit.getToolkit().checkFxUserThread(); + Toolkit.getToolkit().enterNestedEventLoop(this); + + return Optional.ofNullable(chosenProposal); + } + + public void close() { + this.stage.hide(); } private void subscribe(Event e) { @@ -110,9 +129,6 @@ public class ContentProposalPopup implements IContentAssistListener { }; private void updateProposals() { - if( this.proposalApplyInProgress ) { - return; - } List<ICompletionProposal> list = this.proposalComputer.apply(new ContentAssistContextData(this.offset,this.viewer.getDocument()/*,prefix*/)); if( ! list.isEmpty() ) { this.proposalList.setItems(FXCollections.observableArrayList(list)); @@ -125,25 +141,15 @@ public class ContentProposalPopup implements IContentAssistListener { } private void cancelProposal() { - this.stage.hide(); + this.chosenProposal = null; + close(); } private void applySelectedProposal() { - try { - this.proposalApplyInProgress = true; - ICompletionProposal selectedItem = this.proposalList.getSelectionModel().getSelectedItem(); - if( selectedItem != null ) { - IDocument document = this.viewer.getDocument(); - selectedItem.apply(document); - - this.viewer.getTextWidget().setSelection(selectedItem.getSelection(document)); - this.stage.hide(); - - this.fContentAssistant.showContextInformation(selectedItem.getContextInformation(), offset); - } - } finally { - this.proposalApplyInProgress = false; - } + ICompletionProposal selectedItem = this.proposalList.getSelectionModel().getSelectedItem(); + + this.chosenProposal = selectedItem; + close(); } private void handleMouseClicked(MouseEvent event) { @@ -227,6 +233,12 @@ public class ContentProposalPopup implements IContentAssistListener { this.stage.setOnHidden((o) -> { this.stage = null; }); + + this.stage.showingProperty().addListener((x, o, n) -> { + if (!n) { + Toolkit.getToolkit().exitNestedEventLoop(this, null); + } + }); } } diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContextInformationPopup.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContextInformationPopup.java index 7c9c56143..b3c03786a 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContextInformationPopup.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/contentassist/ContextInformationPopup.java @@ -16,6 +16,7 @@ import java.util.Stack; import javax.swing.text.html.HTMLDocument.HTMLReader.HiddenAction; import org.eclipse.fx.text.ui.ITextViewer; +import org.eclipse.fx.ui.controls.styledtext.StyledTextArea.LineLocation; import org.eclipse.fx.ui.controls.styledtext.VerifyEvent; import javafx.geometry.Point2D; @@ -124,13 +125,15 @@ class ContextInformationPopup implements IContentAssistListener { if( viewer.getTextWidget().getScene() != null ) { fContextInfoPopup.getScene().getStylesheets().setAll(viewer.getTextWidget().getScene().getStylesheets()); } - }); + if( viewer.getTextWidget().getScene() != null ) { + fContextInfoPopup.getScene().getStylesheets().setAll(viewer.getTextWidget().getScene().getStylesheets()); + } fRoot = new BorderPane(); fRoot.getStyleClass().add("styled-text-hover"); fContent = new Label(); fRoot.setCenter(fContent); - fContent.getStyleClass().add("styled-text-hover-text"); + fContent.getStyleClass().add("context-info"); fContextInfoPopup.getScene().setRoot(fRoot); } @@ -155,10 +158,12 @@ class ContextInformationPopup implements IContentAssistListener { public void showContextInformation(final IContextInformation info, final int offset) { if (info != null && info.getInformationDisplayString() != null && !info.getInformationDisplayString().isEmpty()) { fContent.setText(info.getInformationDisplayString()); - Point2D locationAtOffset = fViewer.getTextWidget().getLocationAtOffset(offset); + Point2D locationAtOffset = fViewer.getTextWidget().getLocationAtOffset(offset, LineLocation.ABOVE); locationAtOffset = fViewer.getTextWidget().localToScreen(locationAtOffset); + System.err.println("CoNTEXT INFO @ " + locationAtOffset); if (locationAtOffset != null) { - fContextInfoPopup.show(fViewer.getTextWidget().getScene().getWindow(), locationAtOffset.getX(), locationAtOffset.getY()); + double y = locationAtOffset.getY() - fContextInfoPopup.getHeight(); + fContextInfoPopup.show(fViewer.getTextWidget().getScene().getWindow(), locationAtOffset.getX(), y); } } else { diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/HoverPresenter.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/HoverPresenter.java new file mode 100644 index 000000000..0f0d80cc5 --- /dev/null +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/HoverPresenter.java @@ -0,0 +1,16 @@ +package org.eclipse.fx.text.ui.hover; + +import org.eclipse.fx.text.hover.HoverInfo; + +import javafx.geometry.Bounds; +import javafx.geometry.Point2D; +import javafx.scene.Node; + +public interface HoverPresenter { + + int getOrder(); + + boolean isApplicable(Class<? extends HoverInfo> hover); + + Node createHoverContent(HoverInfo hover); +} diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/HoverWindowPresenter.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/HoverWindowPresenter.java new file mode 100644 index 000000000..1d4e0e97f --- /dev/null +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/HoverWindowPresenter.java @@ -0,0 +1,14 @@ +package org.eclipse.fx.text.ui.hover; + +import java.util.List; +import java.util.Set; + +import org.eclipse.fx.text.hover.HoverInfo; + +import javafx.geometry.Bounds; +import javafx.geometry.Point2D; + +public interface HoverWindowPresenter { + void show(Point2D anchor, Bounds bounds, List<HoverInfo> hover); + void hide(); +} diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/internal/DefaultHoverPresenter.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/internal/DefaultHoverPresenter.java new file mode 100644 index 000000000..a52d93094 --- /dev/null +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/internal/DefaultHoverPresenter.java @@ -0,0 +1,44 @@ +package org.eclipse.fx.text.ui.hover.internal; + +import org.eclipse.fx.text.hover.DefaultHoverInfoType; +import org.eclipse.fx.text.hover.HoverInfo; +import org.eclipse.fx.text.ui.hover.HoverPresenter; + +import javafx.geometry.Insets; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; + +public class DefaultHoverPresenter implements HoverPresenter { + + @Override + public int getOrder() { + return -1000; + } + + @Override + public boolean isApplicable(Class<? extends HoverInfo> hover) { + return true; + } + + @Override + public Node createHoverContent(HoverInfo hover) { + HBox b = new HBox(); + b.setSpacing(10); + + Label icon = new Label(); + icon.setText("*"); + + Label content = new Label(); + content.setText(hover.getHoverText()); + + b.getChildren().setAll(icon, content); + + return b; + } + +} diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/internal/DefaultHoverWindowPresenter.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/internal/DefaultHoverWindowPresenter.java new file mode 100644 index 000000000..a685e01ef --- /dev/null +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/hover/internal/DefaultHoverWindowPresenter.java @@ -0,0 +1,134 @@ +package org.eclipse.fx.text.ui.hover.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.eclipse.fx.text.hover.DefaultHoverInfoType; +import org.eclipse.fx.text.hover.HoverInfo; +import org.eclipse.fx.text.ui.hover.HoverPresenter; +import org.eclipse.fx.text.ui.hover.HoverWindowPresenter; + +import javafx.geometry.Bounds; +import javafx.geometry.Point2D; +import javafx.scene.control.Control; +import javafx.scene.layout.VBox; +import javafx.stage.PopupWindow; + +public class DefaultHoverWindowPresenter implements HoverWindowPresenter { + + private List<HoverPresenter> presenters = new ArrayList<>(); + + private final Control parent; + + private final PopupWindow popup; + private final VBox root; + + private List<HoverInfo> currentVisible; + + public DefaultHoverWindowPresenter(Control parent) { + this.parent = parent; + this.popup = new PopupWindow() { + }; + this.popup.setAutoFix(false); + this.popup.setAutoHide(false); + parent.sceneProperty().addListener( e -> { + if( parent.getScene() != null ) { + popup.getScene().getStylesheets().setAll(parent.getScene().getStylesheets()); + } + else { + this.popup.getScene().getStylesheets().clear(); + } + }); + this.root = new VBox(); + this.root.setSpacing(3); + this.root.getStyleClass().add("styled-text-hover"); + this.popup.getScene().setRoot(this.root); +// this.popup.setAnchorLocation(AnchorLocation.CONTENT_BOTTOM_LEFT); + + } + + private Optional<HoverPresenter> findPresenter(HoverInfo hover) { + return this.presenters.stream().filter(p->p.isApplicable(hover.getClass())) + .sorted((a,b)->a.getOrder()-b.getOrder()) + .findFirst(); + } + + private int compare(HoverInfo a, HoverInfo b) { + return a.getHoverText().compareTo(b.getHoverText()); + } + + private void populate(List<HoverInfo> hovers) { + if (this.currentVisible != null && this.currentVisible.equals(hovers)) { + return; + } + this.root.getChildren().clear(); + + List<HoverInfo> errors = hovers.stream().filter(h->h.getType() == DefaultHoverInfoType.ERROR).sorted(this::compare).collect(Collectors.toList()); + List<HoverInfo> warnings = hovers.stream().filter(h->h.getType() == DefaultHoverInfoType.WARNING).sorted(this::compare).collect(Collectors.toList()); + List<HoverInfo> infos = hovers.stream().filter(h->h.getType() == DefaultHoverInfoType.INFO).sorted(this::compare).collect(Collectors.toList()); + List<HoverInfo> docs = hovers.stream().filter(h->h.getType() == DefaultHoverInfoType.DOCUMENTATION).sorted(this::compare).collect(Collectors.toList()); + List<HoverInfo> others = hovers.stream().filter(h->!errors.contains(h) && !warnings.contains(h) && !infos.contains(h) && !docs.contains(h)).sorted(this::compare).collect(Collectors.toList()); + + if (!errors.isEmpty()) { + VBox errorNode = new VBox(); + errorNode.getStyleClass().add("errors"); //$NON-NLS-1$ + errorNode.getChildren().addAll(errors.stream().map(e->findPresenter(e).map(p->p.createHoverContent(e))).filter(x->x.isPresent()).map(x->x.get()).collect(Collectors.toList())); + this.root.getChildren().add(errorNode); + } + + if (!warnings.isEmpty()) { + VBox warnNode = new VBox(); + warnNode.getStyleClass().add("warnings"); //$NON-NLS-1$ + warnNode.getChildren().addAll(warnings.stream().map(e->findPresenter(e).map(p->p.createHoverContent(e))).filter(x->x.isPresent()).map(x->x.get()).collect(Collectors.toList())); + this.root.getChildren().add(warnNode); + } + + if (!infos.isEmpty()) { + VBox infoNode = new VBox(); + infoNode.getStyleClass().add("infos"); //$NON-NLS-1$ + infoNode.getChildren().addAll(infos.stream().map(e->findPresenter(e).map(p->p.createHoverContent(e))).filter(x->x.isPresent()).map(x->x.get()).collect(Collectors.toList())); + this.root.getChildren().add(infoNode); + } + + if (!docs.isEmpty()) { + VBox docsNode = new VBox(); + docsNode.getStyleClass().add("docs"); //$NON-NLS-1$ + docsNode.getChildren().addAll(docs.stream().map(e->findPresenter(e).map(p->p.createHoverContent(e))).filter(x->x.isPresent()).map(x->x.get()).collect(Collectors.toList())); + this.root.getChildren().add(docsNode); + } + + if (!others.isEmpty()) { + VBox othersNode = new VBox(); + othersNode.getStyleClass().add("others"); //$NON-NLS-1$ + othersNode.getChildren().addAll(others.stream().map(e-> + findPresenter(e) + .map(p->p.createHoverContent(e)) + ) + .filter(x->x.isPresent()) + .map(x->x.get()) + .collect(Collectors.toList()) + ); + this.root.getChildren().add(othersNode); + } + + + this.currentVisible = hovers; + } + + @Override + public void show(Point2D screenAnchor, Bounds screenBounds, List<HoverInfo> hover) { + populate(hover); + this.popup.show(this.parent.getScene().getWindow(), screenAnchor.getX(), screenAnchor.getY()); + } + + @Override + public void hide() { + this.popup.hide(); + } + + public void setHoverPresenter(List<HoverPresenter> hoverPresenters) { + this.presenters = hoverPresenters; + } +} diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/AnnotationModelSupport.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/AnnotationModelSupport.java index 70767a407..51eaa697a 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/AnnotationModelSupport.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/AnnotationModelSupport.java @@ -56,6 +56,11 @@ public class AnnotationModelSupport { } @Override + public Object getModel() { + return annotation; + } + + @Override public Range<Integer> getRange() { return range; } diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/InvisibleCharSupport.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/InvisibleCharSupport.java index 3beab1fd3..0f5d6e6b4 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/InvisibleCharSupport.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/InvisibleCharSupport.java @@ -41,6 +41,11 @@ public class InvisibleCharSupport implements IFeature { return symbol; } + @Override + public Object getModel() { + return symbol; + } + public InvisibleCharAnnotation(String symbol, Range range) { this.symbol = symbol; this.range = range; @@ -113,6 +118,11 @@ public class InvisibleCharSupport implements IFeature { t.setText(a.getSymbol()); } + @Override + public String toString() { + return "InvisibleCharAP@" + hashCode(); //$NON-NLS-1$ + } + } public class InvisibleCharAnnotationProvider implements AnnotationProvider { diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/LineNumberSupport.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/LineNumberSupport.java index 2a43a3861..10ba6e662 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/LineNumberSupport.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/LineNumberSupport.java @@ -32,6 +32,12 @@ public class LineNumberSupport implements IFeature { public int getNr() { return nr; } + + @Override + public Object getModel() { + return nr; + } + @Override public int hashCode() { final int prime = 31; @@ -107,7 +113,10 @@ public class LineNumberSupport implements IFeature { }); } - + @Override + public String toString() { + return "LineNrAP@" + hashCode(); //$NON-NLS-1$ + } } @@ -152,4 +161,5 @@ public class LineNumberSupport implements IFeature { }; } + } diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/SimpleSmartIndent.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/SimpleSmartIndent.java new file mode 100644 index 000000000..d3b545ba4 --- /dev/null +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/SimpleSmartIndent.java @@ -0,0 +1,84 @@ +package org.eclipse.fx.text.ui.internal; + +import org.eclipse.fx.text.ui.TextViewer; +import org.eclipse.fx.ui.controls.styledtext.ActionEvent; +import org.eclipse.fx.ui.controls.styledtext.ActionEvent.ActionType; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; + +public class SimpleSmartIndent { + + private TextViewer viewer; + + public SimpleSmartIndent(TextViewer viewer) { + this.viewer = viewer; + this.viewer.getTextWidget().addEventHandler(ActionEvent.ACTION, this::handle); + } + + public void dispose() { + this.viewer.getTextWidget().removeEventHandler(ActionEvent.ACTION, this::handle); + } + + private int findIndentAt(int index) { + String beforeEnter = viewer.getDocument().get().substring(0, index); + + int count = 0; + for (int idx = 0; idx < beforeEnter.length(); idx++) { + char curChar = beforeEnter.charAt(idx); + if (curChar == '{') { + count ++; + } + if (curChar == '}') { + count --; + } + } + + return Math.max(0, count); + } + + private void handle(ActionEvent event) { + + if (event.type == ActionType.NEW_LINE) { + try { + IDocument doc = viewer.getDocument(); + int caret = viewer.getTextWidget().getCaretOffset(); + + int count = findIndentAt(caret); + + int replaceAt = caret; + int replaceLen = 0; + String replace = "\n"; + for (int i = 0; i < count; i++) { + replace += "\t"; + } + + + // endfix + int indent = findIndentAt(caret); + int lineBegin = doc.getLineInformationOfOffset(caret).getOffset(); + String before = doc.get().substring(lineBegin, caret); + if (before.matches("^\\s*}")) { + String tabs = ""; + for (int i = 0; i < indent; i++) { + tabs += "\t"; + } + replaceAt = lineBegin; + replaceLen = before.length(); + replace = tabs + '}' + replace; + } + + + viewer.getDocument().replace(replaceAt, replaceLen, replace); + viewer.getTextWidget().setCaretOffset(caret - replaceLen + replace.length()); + event.consume(); + + + } + catch (BadLocationException e) { + e.printStackTrace(); + } + } + + } + +} diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/TextAnnotationPresenterWrapper.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/TextAnnotationPresenterWrapper.java deleted file mode 100644 index 460bd42ed..000000000 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/TextAnnotationPresenterWrapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.eclipse.fx.text.ui.internal; - -import org.eclipse.fx.ui.controls.styledtext.model.Annotation; -import org.eclipse.fx.ui.controls.styledtext.model.TextAnnotationPresenter; - -import javafx.scene.Node; - -public class TextAnnotationPresenterWrapper implements TextAnnotationPresenter { - - @Override - public boolean isApplicable(Annotation annotation) { - // TODO Auto-generated method stub - return false; - } - - @Override - public Node createNode() { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean isVisible(Annotation annotation) { - // TODO Auto-generated method stub - return false; - } - - @Override - public void updateNode(Node node, Annotation annotation) { - // TODO Auto-generated method stub - - } - -} diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/WrappedLineRulerAnnotationPresenter.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/WrappedLineRulerAnnotationPresenter.java index 9d57b36a7..4e8cb7a76 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/WrappedLineRulerAnnotationPresenter.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/WrappedLineRulerAnnotationPresenter.java @@ -50,11 +50,9 @@ public class WrappedLineRulerAnnotationPresenter implements LineRulerAnnotationP return wrapped.createNode(); } - DoubleProperty TODO = new SimpleDoubleProperty(16); @Override public DoubleProperty getWidth() { - // TODO - return TODO; + return this.wrapped.getWidth(); } @Override @@ -67,4 +65,9 @@ public class WrappedLineRulerAnnotationPresenter implements LineRulerAnnotationP wrapped.updateNode(node, unwrap(annotation)); } + @Override + public String toString() { + return "WAP("+wrapped+")@" + hashCode(); //$NON-NLS-2$ + } + } diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/WrappedTextAnnotationPresenter.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/WrappedTextAnnotationPresenter.java index 986f89e6e..cb2296053 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/WrappedTextAnnotationPresenter.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/internal/WrappedTextAnnotationPresenter.java @@ -48,4 +48,9 @@ public class WrappedTextAnnotationPresenter implements TextAnnotationPresenter { wrapped.updateNode(node, unwrap(annotation)); } + @Override + public String toString() { + return "WAP("+wrapped+")@" + hashCode(); + } + } diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/ILineRulerAnnotationPresenter.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/ILineRulerAnnotationPresenter.java index f992240b6..3be3fc1aa 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/ILineRulerAnnotationPresenter.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/ILineRulerAnnotationPresenter.java @@ -4,6 +4,7 @@ import java.util.Set; import org.eclipse.jface.text.source.Annotation; +import javafx.beans.property.DoubleProperty; import javafx.scene.Node; public interface ILineRulerAnnotationPresenter extends IAnnotationPresenter { @@ -19,4 +20,6 @@ public interface ILineRulerAnnotationPresenter extends IAnnotationPresenter { void updateNode(Node node, Set<Annotation> annotation); LayoutHint getLayoutHint(); + + DoubleProperty getWidth(); } diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/SourceViewer.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/SourceViewer.java index cedd32775..38055949e 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/SourceViewer.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/SourceViewer.java @@ -12,15 +12,23 @@ *******************************************************************************/ package org.eclipse.fx.text.ui.source; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; + +import org.eclipse.fx.text.hover.HoverInfo; import org.eclipse.fx.text.ui.Feature; import org.eclipse.fx.text.ui.ITextViewerExtension2; import org.eclipse.fx.text.ui.TextViewer; +import org.eclipse.fx.text.ui.contentassist.ContentAssistant; import org.eclipse.fx.text.ui.contentassist.IContentAssistant; import org.eclipse.fx.text.ui.internal.AnnotationModelSupport; +import org.eclipse.fx.text.ui.internal.SimpleSmartIndent; import org.eclipse.fx.text.ui.internal.WrappedLineRulerAnnotationPresenter; import org.eclipse.fx.text.ui.internal.WrappedTextAnnotationPresenter; import org.eclipse.fx.text.ui.presentation.IPresentationReconciler; import org.eclipse.fx.text.ui.reconciler.IReconciler; +import org.eclipse.fx.ui.controls.styledtext.VerifyEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ISynchronizable; @@ -40,6 +48,10 @@ public class SourceViewer extends TextViewer implements ISourceViewer, ISourceVi private Annotation fRangeIndicator; + + private BiFunction<IDocument, Integer, Set<HoverInfo>> documentHoverInfoLookup = null; + private Function<Annotation, Set<HoverInfo>> annotationHoverInfoLookup = null; + @Override public void configure(SourceViewerConfiguration configuration) { if (getTextWidget() == null) @@ -55,6 +67,9 @@ public class SourceViewer extends TextViewer implements ISourceViewer, ISourceVi getTextWidget().getStyleClass().add(configuration.getStyleclassName()); + this.documentHoverInfoLookup = configuration::getDocumentHoverInfo; + this.annotationHoverInfoLookup = configuration::getAnnotationHoverInfo; + // if( configuration.getDefaultStylesheet().getValue() != null ) { // getTextWidget().getStylesheets().add(configuration.getDefaultStylesheet().getValue().toExternalForm()); // } @@ -68,6 +83,10 @@ public class SourceViewer extends TextViewer implements ISourceViewer, ISourceVi // } // }); + if (configuration.getContentAssist() instanceof ContentAssistant) { + ((ContentAssistant)configuration.getContentAssist()).setAutoTriggers(configuration.getContentAssistAutoTriggers()); + } + setDocumentPartitioning(configuration.getConfiguredDocumentPartitioning(this)); // install content type independent plug-ins @@ -213,6 +232,9 @@ public class SourceViewer extends TextViewer implements ISourceViewer, ISourceVi // }); } + + new SimpleSmartIndent(this); + } // private Node annotationFactory(StyledTextLine l) { @@ -234,6 +256,22 @@ public class SourceViewer extends TextViewer implements ISourceViewer, ISourceVi // return null; // } + @Override + public Set<HoverInfo> getHoverInfo(int offset) { + if (this.documentHoverInfoLookup != null) { + return this.documentHoverInfoLookup.apply(getDocument(), offset); + } + return super.getHoverInfo(offset); + } + + @Override + public Set<HoverInfo> getHoverInfo(Annotation annotation) { + if (this.annotationHoverInfoLookup != null) { + return this.annotationHoverInfoLookup.apply(annotation); + } + return super.getHoverInfo(annotation); + } + /** * Disposes the visual annotation model. * diff --git a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/SourceViewerConfiguration.java b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/SourceViewerConfiguration.java index 96ea2f7d1..144414baf 100644 --- a/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/SourceViewerConfiguration.java +++ b/bundles/code/org.eclipse.fx.text.ui/src/org/eclipse/fx/text/ui/source/SourceViewerConfiguration.java @@ -14,6 +14,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import org.eclipse.fx.text.hover.HoverInfo; import org.eclipse.fx.text.ui.DefaultUndoManager; import org.eclipse.fx.text.ui.Feature; import org.eclipse.fx.text.ui.ITextHover; @@ -24,6 +25,7 @@ import org.eclipse.fx.text.ui.presentation.PresentationReconciler; import org.eclipse.fx.text.ui.reconciler.IReconciler; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension3; +import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import javafx.beans.property.SetProperty; @@ -73,8 +75,20 @@ public abstract class SourceViewerConfiguration { return null; } + public Set<HoverInfo> getDocumentHoverInfo(IDocument document, int offset) { + return Collections.emptySet(); + } + + public Set<HoverInfo> getAnnotationHoverInfo(Annotation annotation) { + return Collections.emptySet(); + } + public String[] getConfiguredContentTypes(SourceViewer sourceViewer) { return new String[] { IDocument.DEFAULT_CONTENT_TYPE }; } + public String getContentAssistAutoTriggers() { + return "."; + } + } diff --git a/bundles/code/org.eclipse.fx.text/META-INF/MANIFEST.MF b/bundles/code/org.eclipse.fx.text/META-INF/MANIFEST.MF index 51021d3a9..1985562d3 100644 --- a/bundles/code/org.eclipse.fx.text/META-INF/MANIFEST.MF +++ b/bundles/code/org.eclipse.fx.text/META-INF/MANIFEST.MF @@ -4,7 +4,8 @@ Bundle-Name: Basic Text extension Bundle-SymbolicName: org.eclipse.fx.text Bundle-Version: 2.3.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Export-Package: org.eclipse.fx.text.rules;version="2.3.0";x-internal:=true +Export-Package: org.eclipse.fx.text.hover, + org.eclipse.fx.text.rules;version="2.3.0";x-internal:=true Require-Bundle: org.eclipse.text;bundle-version="3.5.300", org.eclipse.equinox.common;bundle-version="3.6.200" Bundle-Vendor: Eclipse.org diff --git a/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/AnnotationHoverProvider.java b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/AnnotationHoverProvider.java new file mode 100644 index 000000000..6267fb675 --- /dev/null +++ b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/AnnotationHoverProvider.java @@ -0,0 +1,8 @@ +package org.eclipse.fx.text.hover; + +import org.eclipse.jface.text.source.Annotation; + +public interface AnnotationHoverProvider { + boolean isApplicable(Class<? extends Annotation> annotationType); + HoverInfo getHoverInfo(Annotation annotation); +} diff --git a/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/DefaultHoverInfoType.java b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/DefaultHoverInfoType.java new file mode 100644 index 000000000..b682f050c --- /dev/null +++ b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/DefaultHoverInfoType.java @@ -0,0 +1,14 @@ +package org.eclipse.fx.text.hover; + +public enum DefaultHoverInfoType implements HoverInfoType { + DOCUMENTATION, + ERROR, + WARNING, + INFO; + + @Override + public String getType() { + return name(); + } + +} diff --git a/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/DocumentHoverProvider.java b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/DocumentHoverProvider.java new file mode 100644 index 000000000..668adc8a5 --- /dev/null +++ b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/DocumentHoverProvider.java @@ -0,0 +1,9 @@ +package org.eclipse.fx.text.hover; + +import java.util.Set; + +import org.eclipse.jface.text.IDocument; + +public interface DocumentHoverProvider { + Set<HoverInfo> getHoverInfo(IDocument document, int offset); +} diff --git a/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/HoverInfo.java b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/HoverInfo.java new file mode 100644 index 000000000..1caeb9724 --- /dev/null +++ b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/HoverInfo.java @@ -0,0 +1,69 @@ +package org.eclipse.fx.text.hover; + +import org.eclipse.jface.text.IRegion; + +public class HoverInfo { + + private final HoverInfoType type; + private final IRegion region; + private final String hoverText; + private final Object hoverModel; + + public HoverInfo(HoverInfoType type, IRegion region, String hoverText, Object hoverModel) { + this.type = type; + this.region = region; + this.hoverText = hoverText; + this.hoverModel = hoverModel; + } + + public HoverInfoType getType() { + return type; + } + + public IRegion getRegion() { + return region; + } + + public String getHoverText() { + return hoverText; + } + + public Object getHoverModel() { + return hoverModel; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((hoverText == null) ? 0 : hoverText.hashCode()); + result = prime * result + ((region == null) ? 0 : region.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + HoverInfo other = (HoverInfo) obj; + if (hoverText == null) { + if (other.hoverText != null) + return false; + } else if (!hoverText.equals(other.hoverText)) + return false; + if (region == null) { + if (other.region != null) + return false; + } else if (!region.equals(other.region)) + return false; + return true; + } + + + + +} diff --git a/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/HoverInfoType.java b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/HoverInfoType.java new file mode 100644 index 000000000..e0a6b0ca8 --- /dev/null +++ b/bundles/code/org.eclipse.fx.text/src/org/eclipse/fx/text/hover/HoverInfoType.java @@ -0,0 +1,5 @@ +package org.eclipse.fx.text.hover; + +public interface HoverInfoType { + String getType(); +} diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/StyledTextArea.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/StyledTextArea.java index 4aaa496ec..d0133df46 100644 --- a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/StyledTextArea.java +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/StyledTextArea.java @@ -1518,18 +1518,26 @@ public class StyledTextArea extends Control { return this.editableProperty; } + public static enum LineLocation { + BELOW, + ABOVE, + CENTER + } + /** * Check the location at the given offset * * @param offset * the offset + * @param locationHint + * hint for y coordinate relative to line * @return the point */ - public @Nullable Point2D getLocationAtOffset(int offset) { + public @Nullable Point2D getLocationAtOffset(int offset, LineLocation locationHint) { if (offset < 0 || offset > getCharCount()) { throw new IllegalArgumentException(); } - return ((StyledTextSkin) getSkin()).getCaretLocation(offset); + return ((StyledTextSkin) getSkin()).getCaretLocation(offset, locationHint); } /** diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/behavior/HoverSupport.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/behavior/HoverSupport.java index 40c470734..367eabcf9 100644 --- a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/behavior/HoverSupport.java +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/behavior/HoverSupport.java @@ -11,10 +11,16 @@ *******************************************************************************/
package org.eclipse.fx.ui.controls.styledtext.behavior;
+import java.util.List;
+
import org.eclipse.fx.ui.controls.Util;
+import org.eclipse.fx.ui.controls.styledtext.events.HoverTarget;
import org.eclipse.fx.ui.controls.styledtext.events.TextHoverEvent;
+import org.eclipse.fx.ui.controls.styledtext.internal.ContentView;
+
import javafx.event.Event;
+import javafx.geometry.Point2D;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.util.Duration;
@@ -27,6 +33,8 @@ public class HoverSupport { private Region control;
private TextHoverEvent lastHover;
+ private TextHoverEvent curHover;
+
/**
* Create a hover support instance
*
@@ -44,7 +52,7 @@ public class HoverSupport { this.control.addEventHandler(MouseEvent.MOUSE_PRESSED, this::onMousePressed);
this.control.addEventHandler(MouseEvent.MOUSE_MOVED, this::onMouseMoved);
this.control.addEventHandler(MouseEvent.MOUSE_EXITED, this::onMouseExited);
- Util.installHoverCallback(this.control, Duration.millis(1000), this::handleHover);
+ Util.installHoverCallback(this.control, Duration.millis(300), this::handleHover);
}
/**
@@ -61,26 +69,38 @@ public class HoverSupport { }
private void onMouseExited(MouseEvent event) {
- if (this.lastHover != null) {
- Event.fireEvent(this.control, new TextHoverEvent(event, -1, -1, -1, "")); //$NON-NLS-1$
- this.lastHover = null;
- }
+ Event.fireEvent(this.control, new TextHoverEvent(event, -1, -1, -1, "")); //$NON-NLS-1$
+ this.lastHover = null;
+ this.curHover = null;
}
private void onMouseMoved(MouseEvent event) {
- if (this.lastHover != null) {
- TextHoverEvent hoverEvent = createHoverEvent(event);
- if (this.lastHover.getOffsetTokenStart() != hoverEvent.getOffsetTokenStart()) {
+// if (this.lastHover != null) {
+// TextHoverEvent hoverEvent = createHoverEvent(event);
+// if (this.lastHover.getOffsetTokenStart() != hoverEvent.getOffsetTokenStart()) {
+// Event.fireEvent(this.control, new TextHoverEvent(event, -1, -1, -1, "")); //$NON-NLS-1$
+// this.lastHover = null;
+// }
+// }
+
+ if (this.curHover != null) {
+ List<HoverTarget> hoverTargets = ((ContentView)this.control).findHoverTargets(new Point2D(event.getX(), event.getY()));
+ TextHoverEvent e = new TextHoverEvent(event, hoverTargets);
+ if (!e.equals(curHover)) {
Event.fireEvent(this.control, new TextHoverEvent(event, -1, -1, -1, "")); //$NON-NLS-1$
- this.lastHover = null;
+ this.curHover = null;
}
}
+
+
+
}
private void onMousePressed(MouseEvent event) {
if (this.lastHover != null) {
Event.fireEvent(this.control, new TextHoverEvent(event, -1, -1, -1, "")); //$NON-NLS-1$
this.lastHover = null;
+ curHover = null;
}
}
@@ -121,15 +141,24 @@ public class HoverSupport { * Create a hover even from the hover mouse event
* @param e the event
*/
- protected void handleHover(MouseEvent e) {
- TextHoverEvent event = createHoverEvent(e);
- if (this.lastHover == null || this.lastHover.getOffsetTokenStart() != event.getOffsetTokenStart()) {
- Event.fireEvent(this.control, event);
- if (event.getOffset() == -1) {
- this.lastHover = null;
- } else {
- this.lastHover = event;
- }
+ protected void handleHover(MouseEvent event) {
+// TextHoverEvent event = createHoverEvent(e);
+// if (this.lastHover == null || this.lastHover.getOffsetTokenStart() != event.getOffsetTokenStart()) {
+// Event.fireEvent(this.control, event);
+// if (event.getOffset() == -1) {
+// this.lastHover = null;
+// } else {
+// this.lastHover = event;
+// }
+// }
+
+
+ List<HoverTarget> hoverTargets = ((ContentView)this.control).findHoverTargets(new Point2D(event.getX(), event.getY()));
+ TextHoverEvent e = new TextHoverEvent(event, hoverTargets);
+ if (this.curHover == null || !e.equals(curHover)) {
+ System.err.println("FIRING HOVER");
+ Event.fireEvent(this.control, e);
+ curHover = e;
}
}
diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/behavior/StyledTextBehavior.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/behavior/StyledTextBehavior.java index 9bfa11465..2f25be6c5 100644 --- a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/behavior/StyledTextBehavior.java +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/behavior/StyledTextBehavior.java @@ -997,6 +997,41 @@ public class StyledTextBehavior { moveCaretRelative(-1, select); } + protected final StyledTextInputAction ACTION_NAVIGATE_TO_LINE = new StyledTextInputAction(this::defaultNavigateToLine); + protected void defaultNavigateToLine() { + try { + +// s-> { +// try { +// int num = Integer.parseInt(s); +// int lineCount = getControl().getContent().getLineCount(); +// if (num < 0 || num >= lineCount) { +// return Optional.of("Must be between 0 and " + lineCount); +// } +// } +// catch (NumberFormatException e) { +// return Optional.of(e.getMessage()); +// } +// return Optional.empty(); +// } + + Optional<Integer> num = ((StyledTextSkin)getControl().getSkin()).fastQuery("Goto Line", "Line Number", Integer::parseInt); + num.ifPresent((n)->defaultNavigateToLine(n-1)); + + } + catch (Exception e) { + e.printStackTrace(); + } + } + + protected void defaultNavigateToLine(int lineIndex) { + if (lineIndex >= 0 && lineIndex <= getControl().getContent().getLineCount()) { + int offset = getControl().getContent().getOffsetAtLine(lineIndex); + getControl().setCaretOffset(offset); + } + } + + /** * Action to move caret right */ @@ -1425,6 +1460,8 @@ public class StyledTextBehavior { // action for insert tab support keyMapping.mapKey(new KeyCombo(TAB), () -> getControl().insert("\t")); //$NON-NLS-1$ + + keyMapping.mapKey(new KeyCombo(KeyCode.L, ControlKey), this.ACTION_NAVIGATE_TO_LINE); } // /** diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/events/HoverTarget.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/events/HoverTarget.java new file mode 100644 index 000000000..87787ee4a --- /dev/null +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/events/HoverTarget.java @@ -0,0 +1,94 @@ +package org.eclipse.fx.ui.controls.styledtext.events;
+
+import com.google.common.collect.Range;
+
+import javafx.geometry.Bounds;
+import javafx.geometry.Point2D;
+
+public class HoverTarget {
+
+ /**
+ * lower left point of the first letter
+ */
+ public final Point2D screenAnchor;
+
+
+ /**
+ * the associated model element.
+ */
+ public final Object model;
+
+ /**
+ * the range in the text
+ */
+ public final Range<Integer> textRange;
+
+ /**
+ * bounds of text in screen coordinates.
+ * <p>
+ * if the text spans over multiple lines, this is <code>null</code>
+ * </p>
+ *
+ */
+ public final Bounds screenBounds;
+
+
+
+ public HoverTarget(Object model, Range<Integer> textRange, Point2D screenAnchor, Bounds screenBounds) {
+ this.screenAnchor = screenAnchor;
+ this.model = model;
+ this.textRange = textRange;
+ this.screenBounds = screenBounds;
+ }
+
+ @Override
+ public String toString() {
+ return "HoverTarget(@"+textRange+", "+model+")";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((model == null) ? 0 : model.hashCode());
+ result = prime * result + ((screenAnchor == null) ? 0 : screenAnchor.hashCode());
+ result = prime * result + ((screenBounds == null) ? 0 : screenBounds.hashCode());
+ result = prime * result + ((textRange == null) ? 0 : textRange.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ HoverTarget other = (HoverTarget) obj;
+ if (model == null) {
+ if (other.model != null)
+ return false;
+ } else if (!model.equals(other.model))
+ return false;
+ if (screenAnchor == null) {
+ if (other.screenAnchor != null)
+ return false;
+ } else if (!screenAnchor.equals(other.screenAnchor))
+ return false;
+ if (screenBounds == null) {
+ if (other.screenBounds != null)
+ return false;
+ } else if (!screenBounds.equals(other.screenBounds))
+ return false;
+ if (textRange == null) {
+ if (other.textRange != null)
+ return false;
+ } else if (!textRange.equals(other.textRange))
+ return false;
+ return true;
+ }
+
+
+
+}
diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/events/TextHoverEvent.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/events/TextHoverEvent.java index 689552f4f..158dcd5c6 100644 --- a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/events/TextHoverEvent.java +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/events/TextHoverEvent.java @@ -10,6 +10,12 @@ *******************************************************************************/
package org.eclipse.fx.ui.controls.styledtext.events;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import com.google.common.collect.Range;
+
import javafx.event.Event;
import javafx.event.EventType;
import javafx.scene.input.MouseEvent;
@@ -40,6 +46,29 @@ public class TextHoverEvent extends MouseEvent { private final String tokenText;
private final int offset;
+ private final List<HoverTarget> hoverTargets;
+
+
+
+ public TextHoverEvent(MouseEvent source, List<HoverTarget> hoverTargets) {
+ super(HOVER, source.getSceneX(), source.getSceneY(), source.getScreenX(), source.getScreenY(), source.getButton(), source.getClickCount(), source.isShiftDown(), source.isControlDown(), source.isAltDown(), source.isMetaDown(), source.isPrimaryButtonDown(), source.isMiddleButtonDown(),
+ source.isSecondaryButtonDown(), source.isSynthesized(), source.isPopupTrigger(), source.isStillSincePress(), source.getPickResult());
+
+ this.hoverTargets = Collections.unmodifiableList(hoverTargets);
+
+ Optional<Range<Integer>> range = this.hoverTargets.stream().map(t->t.textRange).findFirst();
+
+ // TODO remove me
+ this.tokenText = null;
+ this.offsetTokenStart = range.isPresent() ? range.get().lowerEndpoint() : 0;;
+ this.offsetTokenEnd = range.isPresent() ? range.get().upperEndpoint() : 0;;
+ this.offset = range.isPresent() ? range.get().lowerEndpoint() : 0;
+ }
+
+ public List<HoverTarget> getHoverTargets() {
+ return this.hoverTargets;
+ }
+
/**
* Create a new hover event from the original mouse event
*
@@ -64,6 +93,8 @@ public class TextHoverEvent extends MouseEvent { this.offsetTokenEnd = offsetTokenEnd;
this.tokenText = tokenText;
this.offset = offset;
+
+ this.hoverTargets = null;
}
/**
@@ -96,4 +127,48 @@ public class TextHoverEvent extends MouseEvent { public int getOffset() {
return this.offset;
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((hoverTargets == null) ? 0 : hoverTargets.hashCode());
+ result = prime * result + offset;
+ result = prime * result + offsetTokenEnd;
+ result = prime * result + offsetTokenStart;
+ result = prime * result + ((tokenText == null) ? 0 : tokenText.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TextHoverEvent other = (TextHoverEvent) obj;
+ if (hoverTargets == null) {
+ if (other.hoverTargets != null)
+ return false;
+ } else if (!hoverTargets.equals(other.hoverTargets))
+ return false;
+ if (offset != other.offset)
+ return false;
+ if (offsetTokenEnd != other.offsetTokenEnd)
+ return false;
+ if (offsetTokenStart != other.offsetTokenStart)
+ return false;
+ if (tokenText == null) {
+ if (other.tokenText != null)
+ return false;
+ } else if (!tokenText.equals(other.tokenText))
+ return false;
+ return true;
+ }
+
+
+
+
}
diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/ContentView.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/ContentView.java index 0c429ec2a..6e6f0eb9e 100644 --- a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/ContentView.java +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/ContentView.java @@ -10,20 +10,25 @@ *******************************************************************************/
package org.eclipse.fx.ui.controls.styledtext.internal;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.eclipse.fx.ui.controls.Util;
import org.eclipse.fx.ui.controls.styledtext.StyledTextArea;
+import org.eclipse.fx.ui.controls.styledtext.StyledTextArea.LineLocation;
import org.eclipse.fx.ui.controls.styledtext.StyledTextContent;
import org.eclipse.fx.ui.controls.styledtext.StyledTextContent.TextChangeListener;
+import org.eclipse.fx.ui.controls.styledtext.events.HoverTarget;
import org.eclipse.fx.ui.controls.styledtext.TextChangedEvent;
import org.eclipse.fx.ui.controls.styledtext.TextChangingEvent;
import org.eclipse.fx.ui.controls.styledtext.TextSelection;
@@ -170,6 +175,18 @@ public class ContentView extends Pane { }
+
+ public List<HoverTarget> findHoverTargets(Point2D localLocation) {
+ ContiguousSet<Integer> visibleIndexes = ContiguousSet.create(ContentView.this.visibleLines.get(), DiscreteDomain.integers());
+ return visibleIndexes.stream()
+ .map(lineIndex->getVisibleNode(lineIndex))
+ .filter(x->x.isPresent())
+ .filter(x->x.get().getBoundsInParent().contains(localLocation))
+ .flatMap(x->x.get().findHoverTargets(x.get().parentToLocal(localLocation)).stream())
+ .collect(Collectors.toList());
+ }
+
+
// protected void permutate(int a, int b) {
// LineNode nodeA = existingNodes.get(a);
// LineNode nodeB = existingNodes.get(b);
@@ -181,6 +198,10 @@ public class ContentView extends Pane { // }
}
+ public List<HoverTarget> findHoverTargets(Point2D localLocation) {
+ return this.lineLayer.findHoverTargets(localLocation);
+ }
+
private StackPane contentBody = new StackPane();
private LineLayer lineLayer = new LineLayer(()->new LineNode(), (n, m)->{
@@ -812,13 +833,22 @@ public class ContentView extends Pane { // }
- public Optional<Point2D> getLocationInScene(int globalOffset) {
+ public Optional<Point2D> getLocationInScene(int globalOffset, LineLocation locationHint) {
+ applyCss();
+ layout();
+
int lineIndex = getContent().getLineAtOffset(globalOffset);
Optional<LineNode> node = this.lineLayer.getVisibleNode(lineIndex);
return node.map(n->{
double x = n.getCharLocation(globalOffset - n.getStartOffset());
- Point2D p = new Point2D(x, 0);
+ double y = 0;
+ switch (locationHint) {
+ case BELOW: y = 0; break;
+ case ABOVE: y = -getLineHeight(); break;
+ case CENTER: y = -getLineHeight() / 2.0; break;
+ }
+ Point2D p = new Point2D(x, y);
return n.localToScene(p);
});
}
diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/LineNode.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/LineNode.java index efe3ab490..11b6830bb 100644 --- a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/LineNode.java +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/LineNode.java @@ -11,6 +11,8 @@ package org.eclipse.fx.ui.controls.styledtext.internal;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -20,13 +22,17 @@ import java.util.Map.Entry; import java.util.Set;
import java.util.stream.Collectors;
+import org.eclipse.fx.ui.controls.styledtext.events.HoverTarget;
import org.eclipse.fx.ui.controls.styledtext.model.TextAnnotation;
import org.eclipse.fx.ui.controls.styledtext.model.TextAnnotationPresenter;
+import com.google.common.collect.Range;
+
import javafx.animation.Animation;
import javafx.animation.Animation.Status;
import javafx.animation.FadeTransition;
import javafx.animation.Interpolator;
+import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Node;
@@ -201,6 +207,40 @@ public class LineNode extends StackPane { return b.toString();
}
+ private int getStartOffset(Segment segment) {
+ int result = 0;
+ for (Segment s : this.currentContent) {
+ if (s == segment) {
+ break;
+ }
+ result += s.text.length();
+ }
+ return result;
+ }
+
+ private int getLength(Segment segment) {
+ return segment.text.length();
+ }
+
+ public Collection<? extends HoverTarget> findHoverTargets(Point2D localLocation) {
+ for (TextNode t : this.currentTextNodes) {
+ Bounds segmentBounds = t.getBoundsInParent();
+ if (segmentBounds.contains(localLocation)) {
+ Segment segment = this.currentContent.get(currentTextNodes.indexOf(t));
+ Point2D anchor = new Point2D(segmentBounds.getMinX(), segmentBounds.getMaxY());
+
+ int segmentBegin = getStartOffset(segment);
+ int segmentEnd = segmentBegin + getLength(segment);
+ Range<Integer> range = Range.closed(segmentBegin, segmentEnd);
+
+ HoverTarget segmentTarget = new HoverTarget(segment, toGlobal(range), localToScreen(anchor), localToScreen(segmentBounds));
+ return Collections.singletonList(segmentTarget);
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
}
public class SelectionLayer extends Region {
@@ -460,6 +500,22 @@ public class LineNode extends StackPane { }
}
+ public Collection<? extends HoverTarget> findHoverTargets(Point2D localLocation) {
+ return this.usedNodes.entrySet().stream()
+ .filter(e->e.getValue().getBoundsInParent().contains(localLocation))
+ .map(e->{
+ TextAnnotation annotation = e.getKey();
+
+ Bounds bounds = e.getValue().getBoundsInLocal();
+ Point2D anchor = new Point2D(bounds.getMinX(), bounds.getMaxY());
+
+ HoverTarget annotationTarget = new HoverTarget(annotation, toGlobal(annotation.getRange()), e.getValue().localToScreen(anchor), e.getValue().localToScreen(bounds));
+
+ return annotationTarget;
+ })
+ .collect(Collectors.toList());
+ }
+
}
Map<TextAnnotationPresenter, AnnotationOverlay> overlays = new HashMap<>();
@@ -510,6 +566,25 @@ public class LineNode extends StackPane { super.layoutChildren();
}
+
+ public Collection<? extends HoverTarget> findHoverTargets(Point2D localLocation) {
+ List<HoverTarget> hoverTargets = new ArrayList<>();
+ for (AnnotationOverlay overlay : this.overlays.values()) {
+ hoverTargets.addAll(overlay.findHoverTargets(localLocation));
+ }
+ return hoverTargets;
+ }
+
+ }
+
+ protected Range<Integer> toGlobal(Range<Integer> range) {
+ int lineOffset = this.lineHelper.getOffset(index);
+ return Range.range(lineOffset + range.lowerEndpoint(), range.lowerBoundType(), lineOffset + range.upperEndpoint(), range.upperBoundType());
+ }
+
+ protected Range<Integer> toLocal(Range<Integer> range) {
+ int lineOffset = - this.lineHelper.getOffset(index);
+ return Range.range(lineOffset + range.lowerEndpoint(), range.lowerBoundType(), lineOffset + range.upperEndpoint(), range.upperBoundType());
}
TextLayer textLayer = new TextLayer();
@@ -656,6 +731,13 @@ public class LineNode extends StackPane { return this.textLayer.getCharLocation(charOffset);
}
+ public List<HoverTarget> findHoverTargets(Point2D localLocation) {
+ List<HoverTarget> results = new ArrayList<>();
+ results.addAll(this.textLayer.findHoverTargets(localLocation));
+ results.addAll(this.annotationLayer.findHoverTargets(localLocation));
+ return results;
+ }
+
/**
* Check if the offset is between the start and end
*
diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/LineRuler.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/LineRuler.java index 71bee41e2..670871f00 100644 --- a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/LineRuler.java +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/internal/LineRuler.java @@ -28,7 +28,8 @@ import javafx.scene.Node; public class LineRuler extends VerticalLineFlow<Integer, Annotation>{
private LineRulerAnnotationPresenter.LayoutHint h;
- private DoubleProperty absoluteMinWidth = new SimpleDoubleProperty(this, "absoluteMinWidth"); //$NON-NLS-1$
+
+ private DoubleProperty fixedWidth = new SimpleDoubleProperty(this, "fixedWidth"); //$NON-NLS-1$
private DoubleProperty yOffset = new SimpleDoubleProperty(this, "yOffset"); //$NON-NLS-1$
@@ -44,11 +45,16 @@ public class LineRuler extends VerticalLineFlow<Integer, Annotation>{ @Override
protected double computeMinWidth(double height) {
- return Math.max(this.absoluteMinWidth.get(), super.computePrefWidth(height));
+ return this.fixedWidth.get();
+ }
+
+ @Override
+ protected double computeMaxWidth(double height) {
+ return this.fixedWidth.get();
}
- public DoubleProperty absoluteMinWidthProperty() {
- return this.absoluteMinWidth;
+ public DoubleProperty fixedWidthProperty() {
+ return this.fixedWidth;
}
@Override
diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/model/Annotation.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/model/Annotation.java index 01a7e1db2..a31b47fe4 100644 --- a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/model/Annotation.java +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/model/Annotation.java @@ -11,4 +11,5 @@ package org.eclipse.fx.ui.controls.styledtext.model;
public interface Annotation {
+ public Object getModel();
}
diff --git a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/skin/StyledTextSkin.java b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/skin/StyledTextSkin.java index 53c8aa960..081583e8f 100644 --- a/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/skin/StyledTextSkin.java +++ b/bundles/runtime/org.eclipse.fx.ui.controls/src/org/eclipse/fx/ui/controls/styledtext/skin/StyledTextSkin.java @@ -27,6 +27,7 @@ import org.eclipse.fx.ui.controls.styledtext.StyledTextArea; import org.eclipse.fx.ui.controls.styledtext.StyledTextContent.TextChangeListener; import org.eclipse.fx.ui.controls.styledtext.TextChangedEvent; import org.eclipse.fx.ui.controls.styledtext.TextChangingEvent; +import org.eclipse.fx.ui.controls.styledtext.StyledTextArea.LineLocation; import org.eclipse.fx.ui.controls.styledtext.behavior.StyledTextBehavior; import org.eclipse.fx.ui.controls.styledtext.internal.ContentView; import org.eclipse.fx.ui.controls.styledtext.internal.FXBindUtil; @@ -53,6 +54,7 @@ import javafx.geometry.Insets; import javafx.geometry.Point2D; import javafx.scene.Node; import javafx.scene.control.SkinBase; +import javafx.scene.control.TextInputDialog; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; @@ -272,7 +274,7 @@ public class StyledTextSkin extends SkinBase<StyledTextArea> { // flow.getModel().bindContent(this.getModel()); - flow.absoluteMinWidthProperty().bind(ap.getWidth()); + flow.fixedWidthProperty().bind(ap.getWidth()); // flow.prefWidthProperty().bind(ap.getWidth()); flow.prefWidthProperty().addListener((x, o, n) -> { @@ -358,6 +360,14 @@ public class StyledTextSkin extends SkinBase<StyledTextArea> { } + public <T> Optional<T> fastQuery(String label, String fieldText, Function<String, T> converter) { + TextInputDialog diag = new TextInputDialog(); + diag.setTitle(label); + diag.setHeaderText(label); + diag.setContentText(fieldText); + return diag.showAndWait().map(converter); + } + private void scrollLineIntoView(int lineIndex) { this.scroller.scrollIntoView(lineIndex); } @@ -388,12 +398,12 @@ public class StyledTextSkin extends SkinBase<StyledTextArea> { * the position * @return the point */ - public Point2D getCaretLocation(int caretPosition) { + public Point2D getCaretLocation(int caretPosition, LineLocation locationHint) { if (caretPosition < 0) { return null; } - Optional<Point2D> location = this.content.getLocationInScene(caretPosition); + Optional<Point2D> location = this.content.getLocationInScene(caretPosition, locationHint); return location.map(l -> this.rootContainer.sceneToLocal(l)).map(l -> new Point2D(l.getX(), l.getY() + this.content.getLineHeight())).orElse(null); |