Skip to content

Commit

Permalink
Merge pull request #149 from bric3/custom-layout-to-fix-issue-46
Browse files Browse the repository at this point in the history
Introduce custom viewport layout to fix container shrinking
  • Loading branch information
bric3 authored Jan 26, 2023
2 parents 4f46f3c + 97334a2 commit 1a25ac2
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,7 @@ private void paintHoveredFrameBorder(
if (viewRect.intersects(frameRect)) {
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2.setColor(frameBorderColor.get());
// TODO use floor / ceil ?
g2.drawRect((int) x, (int) y, (int) w, (int) h);
g2.draw(frameRect);
}
}

Expand Down Expand Up @@ -683,8 +682,10 @@ public ZoomTarget calculateZoomTargetFrame(
* factor = ----------------------------
* frameWidthX * bounds.width
* </pre>
*
* Note that to retrieve the zoom factor one should use {@code 1 / factor}.
*/
private static double getScaleFactor(double visibleWidth, double canvasWidth, double frameWidthX) {
protected static double getScaleFactor(double visibleWidth, double canvasWidth, double frameWidthX) {
return visibleWidth / (canvasWidth * frameWidthX);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
Expand Down Expand Up @@ -226,7 +227,7 @@ public FlamegraphView() {
canvas = new FlamegraphCanvas<>(this);
canvas.putClientProperty(OWNER_KEY, this);
scrollPaneListener = new FlamegraphScrollPaneMouseInputListener<>(canvas);
var scrollPane = new JScrollPane(canvas);
var scrollPane = createScrollPane();
scrollPane.putClientProperty(OWNER_KEY, this);
var layeredScrollPane = JScrollPaneWithBackButton.create(
() -> {
Expand Down Expand Up @@ -265,6 +266,105 @@ public FlamegraphView() {
});
}

private JScrollPane createScrollPane() {
var jScrollPane = new JScrollPane(canvas);
var viewport = new JViewport() {
@Override
protected LayoutManager createLayoutManager() {
return new ViewportLayout() {
private final Dimension oldViewPortSize = new Dimension(); // reusable
private final Dimension flamegraphSize = new Dimension(); // reusable
private final Point flamegraphLocation = new Point(); // reusable

@Override
public void layoutContainer(Container parent) {
// Custom layout code to handle container shrinking.
// The default view port layout asks the preferred size
// of the view.
// But that cannot work since the canvas won;t update
// its width, it receives its size from the layout container.
//
// However, the default algorithm only updates the size
// after it has received the preferred size, or if the
// viewport got bigger.
//
// This code makes the necessary query to the canvas to
// asks if it needs a new size given the viewport width change,
// in order to keep the same zoom factor.
//
// The view location is also updated.

var vp = (JViewport) parent;
var canvas = (FlamegraphCanvas<?>) vp.getView();
int oldVpWidth = oldViewPortSize.width;
var vpSize = vp.getSize(oldViewPortSize);

// view port has been resized
if (vpSize.width != oldVpWidth) {
// if old fg width == old vp width
// the scaleFactor is 1.0
// => recompute the fg size
// if old fg width > old vp width
// the scaleFactor is > 1.0
// => compute the scaleFactor
// => scale the fg size using the current vp width
// if old fg width < old vp width
// ==> do nothing
int oldFlamegraphWidth = flamegraphSize.width;
if (oldFlamegraphWidth == oldVpWidth) {
canvas.updateFlamegraphDimension(
flamegraphSize,
vpSize.width
);

// check view position ?
} else {
// compute scale factor
double scaleFactor = FlamegraphRenderEngine.getScaleFactor(
oldVpWidth,
oldFlamegraphWidth,
1.0
);

// scale the fg size with the new viewport width
canvas.updateFlamegraphDimension(
flamegraphSize,
(int) Math.round(vpSize.width / scaleFactor)
);

}
vp.setViewSize(flamegraphSize);

// if view position X > 0
// the fg is zoomed
// => compute the position ratio
// => apply ratio to the current fg width

int oldFlamegraphX = Math.abs(flamegraphLocation.x);
if (oldFlamegraphX > 0) {
// compute scale factor
double positionRatio = (double) oldFlamegraphX / (double) oldFlamegraphWidth;
flamegraphLocation.x = Math.abs((int) Math.round(positionRatio * flamegraphSize.width));
flamegraphLocation.y = Math.abs(flamegraphLocation.y);

vp.setViewPosition(flamegraphLocation);
}
} else {
super.layoutContainer(parent);
// update the sizes
vp.getSize(oldViewPortSize);
canvas.getSize(flamegraphSize);
canvas.getLocation(flamegraphLocation);
}
}
};
}
};
jScrollPane.setViewport(viewport);
jScrollPane.setViewportView(canvas);
return jScrollPane;
}

/**
* Murky workaround to propagate the background color to the canvas
* since JLayer is final.
Expand Down Expand Up @@ -435,7 +535,9 @@ public void setShowHoveredSiblings(boolean showHoveredSiblings) {
* @return {@code true} if the siblings of the hovered frame are highlighted, {@code false} otherwise.
*/
public boolean isShowHoveredSiblings() {
return canvas.getFlamegraphRenderEngine().map(FlamegraphRenderEngine::isShowHoveredSiblings).orElse(false);
return canvas.getFlamegraphRenderEngine()
.map(FlamegraphRenderEngine::isShowHoveredSiblings)
.orElse(false);
}

/**
Expand Down Expand Up @@ -595,7 +697,7 @@ public List<FrameBox<T>> getFrames() {
* @param value the value.
* @see JComponent#putClientProperty(Object, Object)
*/
public void putClientProperty(String key, Object value) {
public <V> void putClientProperty(String key, V value) {
// value can be null, it means removing the key (see putClientProperty)
canvas.putClientProperty(Objects.requireNonNull(key), value);
}
Expand All @@ -607,8 +709,9 @@ public void putClientProperty(String key, Object value) {
* @return the value
* @see JComponent#getClientProperty(Object)
*/
public Object getClientProperty(String key) {
return canvas.getClientProperty(Objects.requireNonNull(key));
@SuppressWarnings("unchecked")
public <V> V getClientProperty(String key) {
return (V) canvas.getClientProperty(Objects.requireNonNull(key));
}

/**
Expand Down Expand Up @@ -917,11 +1020,35 @@ public void updateUI() {
super.updateUI();
}

@Override
public void doLayout() {
Rectangle bounds = getBounds();
double delta = getParent().getWidth() - getVisibleRect().getWidth();
// TODO capture position in view rect

if(delta < 0) {

}
Point location = getLocation();


super.doLayout();
}

@Override
public void addNotify() {
super.addNotify();
var fgCanvas = this;

fgCanvas.addHierarchyListener(e -> {
boolean b = (e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0;
if (b && !e.getComponent().isDisplayable()) {
fgCanvas.getParent();
// todo
}
});


// Adjust the width of the canvas to the width of the view rect, when
// the scroll bar is made visible, this prevents the horizontal scrollbar
// from appearing on first display, see #96.
Expand Down Expand Up @@ -1026,7 +1153,6 @@ public Dimension getPreferredSize() {
flamegraphWidth,
true
);

preferredSize.width = Math.max(preferredSize.width, flamegraphWidth);
preferredSize.height = Math.max(preferredSize.height, flamegraphHeight);

Expand All @@ -1037,6 +1163,18 @@ public Dimension getPreferredSize() {
return preferredSize;
}

protected Dimension updateFlamegraphDimension(Dimension dimension, int flamegraphWidth) {
var flamegraphHeight = flamegraphRenderEngine.computeVisibleFlamegraphHeight(
(Graphics2D) getGraphics(),
flamegraphWidth,
true
);

dimension.width = flamegraphWidth;
dimension.height = flamegraphHeight;
return dimension;
}

@Override
protected void paintComponent(Graphics g) {
long start = System.currentTimeMillis();
Expand Down

0 comments on commit 1a25ac2

Please sign in to comment.