package com.yoursway.ide.worksheet.internal.view;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.CoolStyledText;
import org.eclipse.swt.widgets.Display;
import com.mkalugin.swthell.CoolScrollBar;
import com.mkalugin.swthell.CoolScrollBarStyledTextBinding;
import com.yoursway.ide.worksheet.WorksheetStyle;
import com.yoursway.swt.animations.SizeAndAlphaAnimation;
import com.yoursway.swt.animations.SizeAndAlphaAnimationApplier;
import com.yoursway.swt.styledtext.extended.Inset;
import com.yoursway.swt.styledtext.extended.InsetSite;
import com.yoursway.swt.styledtext.extended.ResizeListener;
import com.yoursway.utils.annotations.DeadlockWarningBlocksOnUIThread;
import com.yoursway.utils.annotations.UseFromAnyThread;
import com.yoursway.utils.annotations.UseFromUIThread;
public class ResultInset implements Inset {
private final WorksheetStyle style;
private StyledText embeddedText;
private CoolScrollBar scrollBar;
private InsetSite site;
private final SizeAndAlphaAnimation animation;
private int alpha;
private boolean pending;
private int newLines = 0;
@UseFromAnyThread
public ResultInset(WorksheetStyle style) {
if (style == null)
throw new NullPointerException("settings is null");
this.style = style;
animation = new SizeAndAlphaAnimation();
}
@UseFromUIThread
public void init(Composite composite, InsetSite site) {
initSite(site);
initComposite(composite);
initEmbeddedText(composite);
initAnimation(composite);
}
private void initSite(final InsetSite site) {
if (site == null)
throw new NullPointerException("site is null");
this.site = site;
site.addResizeListener(new ResizeListener() {
@UseFromUIThread
public void resized(Point size) {
updateSize(true);
}
});
}
private void initComposite(final Composite composite) {
composite.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
e.gc.setBackground(style.resultInsetColor());
Point size = composite.getSize();
int radius = 10, left = 10; //! magic
e.gc.fillRoundRectangle(left, 0, size.x - left, size.y, radius, radius);
e.gc.setBackground(site.getBackground());
e.gc.setAlpha(255 - alpha);
e.gc.fillRectangle(0, 0, size.x, size.y);
}
});
}
private void initEmbeddedText(Composite composite) {
embeddedText = new CoolStyledText(composite, SWT.MULTI | SWT.WRAP);
embeddedText.setFont(style.resultFont());
embeddedText.setBackground(style.resultInsetColor());
embeddedText.setForeground(style.outputColor());
embeddedText.setEditable(false);
embeddedText.setLocation(18, 5); //! magic
scrollBar = new CoolScrollBar(composite, SWT.NO_BACKGROUND, true, style.resultScrollbarColor());
scrollBar.setBeginMargin(2);
scrollBar.setEndMargin(2);
new CoolScrollBarStyledTextBinding(embeddedText, scrollBar, composite);
scrollBar.setCursor(style.scrollbarCursor());
((CoolStyledText) embeddedText).setScrollBar(scrollBar);
setText("", false);
embeddedText.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
e.gc.setBackground(site.getBackground());
e.gc.setAlpha(255 - alpha);
Point size = embeddedText.getSize();
e.gc.fillRectangle(0, 0, size.x, size.y);
}
});
}
private void initAnimation(final Composite composite) {
alpha = 0;
animation.targetAlpha(255);
animation.start(new SizeAndAlphaAnimationApplier() {
@UseFromAnyThread
@DeadlockWarningBlocksOnUIThread
public void updateSize(final int width, final int height) {
if (composite.isDisposed())
return;
composite.getDisplay().syncExec(new Runnable() {
public void run() {
if (composite.isDisposed())
return;
composite.setSize(width, height);
//! magic
scrollBar.setLocation(width - 12, 0);
scrollBar.setSize(12, height);
}
});
}
@UseFromAnyThread
@DeadlockWarningBlocksOnUIThread
public void updateAlpha(final int alpha) {
ResultInset.this.alpha = alpha;
if (composite.isDisposed())
return;
composite.getDisplay().syncExec(new Runnable() {
public void run() {
if (composite.isDisposed())
return;
composite.redraw();
redraw();
}
});
}
@UseFromAnyThread
@DeadlockWarningBlocksOnUIThread
public boolean visible() {
if (composite.isDisposed())
return false;
final boolean[] visible = new boolean[1];
Display display = composite.getDisplay();
if (display.isDisposed())
return false;
display.syncExec(new Runnable() { //!
public void run() {
if (composite.isDisposed()) {
visible[0] = false;
return;
}
visible[0] = composite.getVisible();
}
});
return visible[0];
}
});
}
@UseFromUIThread
public void dispose() {
if (!animation.isDisposed()) {
animation.dispose();
}
if (embeddedText != null) {
if (!embeddedText.isDisposed())
embeddedText.dispose();
embeddedText = null;
}
}
@UseFromAnyThread
public boolean isDisposed() {
//! (embeddedText == null) before init too
return embeddedText == null || embeddedText.isDisposed(); //?
}
@UseFromAnyThread
@DeadlockWarningBlocksOnUIThread
private void setText(final String text, boolean pending) {
newLines = 0;
becomeUpdated();
this.pending = pending;
embeddedText.getDisplay().syncExec(new Runnable() {
public void run() {
if (isDisposed())
return;
embeddedText.setText(text);
updateSize(false);
}
});
}
@UseFromAnyThread
@DeadlockWarningBlocksOnUIThread
public void append(String text, final boolean error) {
pending = false;
StringBuilder sb = new StringBuilder();
for (; newLines > 0; newLines--) {
sb.append('\n');
}
int end = text.length();
while (end > 0 && text.charAt(end - 1) == '\n') {
end--;
newLines++;
}
sb.append(text, 0, end);
final String t = sb.toString();
embeddedText.getDisplay().syncExec(new Runnable() {
public void run() {
if (isDisposed())
return;
int start = embeddedText.getCharCount();
embeddedText.append(t);
if (error) {
StyleRange style = ResultInset.this.style.errorStyle(start, t.length());
embeddedText.setStyleRange(style);
}
updateSize(false);
}
});
}
@UseFromUIThread
private void updateSize(boolean siteResized) {
if (isDisposed())
return;
if (pending && embeddedText.getSize().y > 0)
return;
Point size = embeddedText.computeSize(SWT.DEFAULT, SWT.DEFAULT);
int maxWidth = site.clientAreaSize().x - 50;
if (maxWidth < 50)
maxWidth = 50; //! hack, magic
if (size.x > maxWidth)
size = embeddedText.computeSize(maxWidth, SWT.DEFAULT);
int maxHeight = site.clientAreaSize().y / 3; //! magic
if (maxHeight > 100)
maxHeight = 100; //! magic
if (size.y > maxHeight)
size.y = maxHeight;
embeddedText.setSize(size);
animation.targetSize(size.x + 35, size.y + 10); //! magic
if (siteResized)
animation.instantWidth();
}
@UseFromAnyThread
public void becomeObsolete() {
animation.targetAlpha(129);
}
@UseFromAnyThread
private void becomeUpdated() {
animation.targetAlpha(255);
}
@UseFromUIThread
public void redraw() { //?
if (isDisposed())
return;
embeddedText.redraw();
}
@UseFromAnyThread
public void becomeWaiting() {
setText("...", true);
}
@UseFromAnyThread
public void reset() {
setText("", true);
}
}