Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Existence of SplitPane-Handle isn't clear to the user #179

Closed
Bios-Marcel opened this issue Sep 24, 2020 · 14 comments
Closed

Existence of SplitPane-Handle isn't clear to the user #179

Bios-Marcel opened this issue Sep 24, 2020 · 14 comments
Milestone

Comments

@Bios-Marcel
Copy link
Contributor

Unless a user knows that a splitpane is present, it's not possible to see it, as there's currently no handle.
I'd suggest adding a handle. We currently add three dots that use the same colors as the disabled font color.

We'd like to properly implement this though. We've been using this hack for a while and would like to contribute this.
Would you be okay with that? If so, what color should we use for it and what should the size of the handle and the icons be?

This is the code we've been using and an image of what it looks like:

grafik

Icon:

public class SplitPaneDividerBorderIcon implements Icon
{
  private static final Color CIRCLE_COLOR  = UIManager.getColor( "Label.disabledForeground" );
  private static final int   CIRCLE_AMOUNT = 3;
  private static final int   SIZE          = SwingGuiUtils.scaleToDpi( 16 );
  //Wir haben 3 Kreise und wollen, dass dessen Durchmesser 25% der Breite des Icons entsprechen.
  private static final int            CIRCLE_SIZE     = SIZE / 4;
  private static final int            GAP             = (SIZE - (CIRCLE_SIZE * CIRCLE_AMOUNT)) / (CIRCLE_AMOUNT - 1);
  private static final RenderingHints RENDERING_HINTS = new RenderingHints(
      RenderingHints.KEY_ANTIALIASING,
      RenderingHints.VALUE_ANTIALIAS_ON );

  private final boolean vertical;
  private final int     height;
  private final int     width;

  /**
   * @param vertical true = vertical
   */
  public SplitPaneDividerBorderIcon( final boolean vertical )
  {
    this.vertical = vertical;
    height = vertical ? SIZE : (CIRCLE_SIZE * 3);
    width = vertical ? (CIRCLE_SIZE * 3) : SIZE;
  }

  @Override
  public void paintIcon( final Component c, final Graphics g, final int x, final int y )
  {
    if ( g instanceof Graphics2D )
    {
      ((Graphics2D) g).setRenderingHints( RENDERING_HINTS );
    }
    g.setColor( CIRCLE_COLOR );
    final Rectangle bounds = c.getBounds();
    int xPosition = bounds.width / 2 - (CIRCLE_SIZE / 2);
    int yPosition = bounds.height / 2 - (CIRCLE_SIZE / 2);
    if ( !vertical )
    {
      //Aus irgendeinem Grund ist dieses Icon nicht zentriert, daher rechnen wir ein Feel-Good-Wert hinzu.
      yPosition -= SwingGuiUtils.scaleToDpi( 8 );
      for ( int i = 0; i < CIRCLE_AMOUNT; i++ )
      {
        g.fillOval( xPosition, yPosition + (i * GAP + i * CIRCLE_SIZE), CIRCLE_SIZE, CIRCLE_SIZE );
      }
    }
    else
    {
      //Aus irgendeinem Grund ist dieses Icon nicht zentriert, daher rechnen wir ein Feel-Good-Wert hinzu.
      xPosition -= SwingGuiUtils.scaleToDpi( 8 );
      for ( int i = 0; i < CIRCLE_AMOUNT; i++ )
      {
        g.fillOval( xPosition + (i * GAP + i * CIRCLE_SIZE), yPosition, CIRCLE_SIZE, CIRCLE_SIZE );
      }
    }
  }

  @Override
  public int getIconHeight()
  {
    return height;
  }

  @Override
  public int getIconWidth()
  {
    return width;
  }
}

Border:

public static class SplitPaneDividerBorder implements Border
{
  private final Icon iconVertical;
  private final Icon iconHorizontal;

  private final Insets insets;

  public SplitPaneDividerBorder( final Icon iconVertical, final Icon iconHorizontal )
  {
    this.iconVertical = iconVertical;
    this.iconHorizontal = iconHorizontal;

    //Insets sind null, da der Border keine Größe haben soll, sondern lediglich zentriert ein Icon zeichnen soll.
    insets = new Insets( 0, 0, 0, 0 );
  }

  @Override
  public void paintBorder( final @Nullable Component component, final @Nullable Graphics graphics,
                           final int x, final int y, final int width, final int height )
  {
    if ( component instanceof BasicSplitPaneDivider )
    {
      final BasicSplitPaneDivider divider = (BasicSplitPaneDivider) component;
      final int orientation = getDividerOrientation( divider );

      if ( orientation == JSplitPane.VERTICAL_SPLIT )
      {
        iconVertical.paintIcon( divider, graphics, 0, 0 );
      }
      else if ( orientation == JSplitPane.HORIZONTAL_SPLIT )
      {
        iconHorizontal.paintIcon( divider, graphics, 0, 0 );
      }
    }
  }

  private static int getDividerOrientation( final BasicSplitPaneDivider divider )
  {
    final int orientation;
    if ( divider.getParent() instanceof JSplitPane )
    {
      final JSplitPane parentSplitPane = (JSplitPane) divider.getParent();
      orientation = parentSplitPane.getOrientation();
    }
    else
    {
      throw new UnsupportedOperationException( "Ein SplitPaneDivider braucht eine SplitPane." );
    }

    return orientation;
  }

  @Override
  public Insets getBorderInsets( final @Nullable Component c )
  {
    return insets;
  }


  @Override
  public boolean isBorderOpaque()
  {
    return true;
  }
}

To use it:

    final Icon iconVertical = new SplitPaneDividerBorderIcon( true );
    final Icon iconHorizontal = new SplitPaneDividerBorderIcon( false );
    UIManager.put( "SplitPaneDivider.border", new SplitPaneDividerBorder( iconVertical, iconHorizontal ) );
    UIManager.put( "SplitPane.dividerSize", iconVertical.getIconWidth() );
@Bios-Marcel Bios-Marcel changed the title Existence of SplitPane isn't visible to the user Existence of SplitPane-Handle isn't clear to the user Sep 24, 2020
@k33ptoo
Copy link

k33ptoo commented Oct 18, 2020

Very useful. Anyone looking for SwingGuiUtils.scaleToDpi(int) function you can use DPIController class in this repo.

@Bios-Marcel
Copy link
Contributor Author

Yeah, I basically just copied the code over here. I think I didn't even need to do that. I was just wondering whether it would be okay to add these dots as a PR ^^

@k33ptoo
Copy link

k33ptoo commented Oct 18, 2020

I hope the devs will consider. ;-)

@DevCharly
Copy link
Collaborator

Sorry for the late reply...

Yes, these dots would be nice and I'll accept a PR 👍

But then we do not need two extra classes.
The painting can be done directly in class FlatSplitPaneUI.FlatSplitPaneDivider

Just remembered that there is already some code in FlatLaf that paints dots. It is in the toolbar:

image

The code is in FlatToolBarBorder.paintGrip:

protected void paintGrip( Component c, Graphics g, int x, int y, int width, int height ) {
int dotSize = scale( DOT_SIZE );
int gapSize = dotSize;
int gripSize = (dotSize * DOT_COUNT) + ((gapSize * (DOT_COUNT - 1)));
// include toolbar margin in grip position calculation
Insets insets = getBorderInsets( c );
// calculate grip position
boolean horizontal = ((JToolBar)c).getOrientation() == SwingConstants.HORIZONTAL;
if( horizontal ) {
if( c.getComponentOrientation().isLeftToRight() )
x += insets.left - (dotSize * 2);
else
x += width - insets.right + dotSize;
y += Math.round( (height - gripSize) / 2f );
} else {
// vertical
x += Math.round( (width - gripSize) / 2f );
y += insets.top - (dotSize * 2);
}
// paint dots
for( int i = 0; i < DOT_COUNT; i++ ) {
g.fillOval( x, y, dotSize, dotSize );
if( horizontal )
y += dotSize + gapSize;
else
x += dotSize + gapSize;
}
}

You could use method paintGrip as base,
copy it to class FlatSplitPaneUI.FlatSplitPaneDivider,
change dot size to 3,
change dot count to 3,
maybe change gap between dots to 2,
remove right-to-left specific code,
remove insets specific code.
Simply paint dots in center of divider component.

Maybe also increase divider thickness, which is currently 5px and probably to small for 3px dots.
7 could work fine with 3px dots and 2px gap on each side.

Regarding color of dots:
The toolbar grip uses ToolBar.gripColor

To be consistent I would suggest something like:

SplitPaneDivider.gripColor=@icon

@k33ptoo
Copy link

k33ptoo commented Nov 3, 2020

Hi @DevCharly thank you for above, any chance this will be added soon to release.

@Bios-Marcel
Copy link
Contributor Author

I didn't get to do this yet, sorry. It's rather low prio right now, as this basically already works for us 😓

@k33ptoo
Copy link

k33ptoo commented Nov 3, 2020

Hi @Bios-Marcel thanks for the update, I tried your solution but then I got a bug on second monitor on Mac, the icons disappear.

@Bios-Marcel
Copy link
Contributor Author

Oh, that's interesting. When dragging the window over?

@k33ptoo
Copy link

k33ptoo commented Nov 3, 2020

When the window(s) are launched on the second monitor.

@DevCharly
Copy link
Collaborator

I'm working on it...

@Bios-Marcel
Copy link
Contributor Author

Bios-Marcel commented Nov 4, 2020

@DevCharly Stupid question on the side: Do yout take donations anywhere?

@j-dimension
Copy link

@Bios-Marcel I've been asking the same not at all stupid question in the past.

No answer from @DevCharly so far, but I am still interested! Being an Open Source author myself, I really would like to express my gratitude for the time and passion that goes into this.

@DevCharly
Copy link
Collaborator

@Bios-Marcel thanks for the offer.
FlatLaf development definitely could need some sponsoring 😃
Until now I've financed it alone...
Please contact me at info (at) formdev (dot) com.
Thanks.

@j-dimension found and answered you Email. Thx.

@DevCharly
Copy link
Collaborator

The splitpane divider grip is implemented in commit 18d8c7d.

image

@Bios-Marcel the divider width is still 5px, but the dots are smaller than in your code.
However you can configure the dot size, dot count, dot gap and dot color:

# allowed values: "grip" (default) or "plain"
SplitPaneDivider.style=grip
SplitPaneDivider.gripColor=@icon
SplitPaneDivider.gripDotCount=3
SplitPaneDivider.gripDotSize={integer}3
SplitPaneDivider.gripGap=2

Following code makes divider wider, dots larger and use 4 dots:

UIManager.put( "SplitPane.dividerSize", 9 );
UIManager.put( "SplitPaneDivider.gripDotCount", 4 );
UIManager.put( "SplitPaneDivider.gripDotSize", 4 );
UIManager.put( "SplitPaneDivider.gripGap", 3 );

image

To disable divider grip use:

UIManager.put( "SplitPaneDivider.style", "plain" );

@DevCharly DevCharly added this to the 0.44 milestone Nov 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants