Custom JScrollPane - Double JScrollBars

P

pek

Is there any way I can customize a JScrollPane and instead of one
horizontal JScrollPane it would show two? Can you point me somewhere?

Thank you.
 
P

pek

Is there any way I can customize a JScrollPane and instead of one
horizontal JScrollPane it would show two? Can you point me somewhere?

Thank you.

Sorry, I meant displaying two horizontal ScrollBars.
 
R

RedGrittyBrick

pek said:
Sorry, I meant displaying two horizontal ScrollBars.

Maybe you want a JSplitPane.

If not, try describing what you want to achieve more clearly.
 
D

David A. Redick

You'll have to override the layout manager.

In the example below, I made the scroll pane display 2 vertical bars.
All I did was resize/reposition the viewport, the vertical and the
horizontal bar.

The outer most vertical bar will still act like that standard horiz
bar.
The inner vertical bar will be the same normal vertical bar.

PS.
Are you sure you really need to do this?
I agree with RGB that you sure have a look at the other default panes
just make sure. =/

---------------------------------------------------------

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class MySPLayout extends ScrollPaneLayout
{
public void layoutContainer(Container parent)
{
hsb.setOrientation(JScrollBar.HORIZONTAL);
super.layoutContainer(parent);


Rectangle hRec = hsb.getBounds();
Rectangle vRec = vsb.getBounds();
Dimension size = viewport.getExtentSize();


// adjust viewport size, add to the bottom, sub from right side
size.width -= vRec.width;
size.height += hRec.height;
viewport.setExtentSize(size);

// get rid of the corner
vRec.height += hRec.height;

// scoot over to make room
vsb.setLocation(vRec.x - vRec.width, vRec.y);
vsb.setSize(vRec.width, vRec.height);

hsb.setOrientation(JScrollBar.VERTICAL);
hsb.setLocation(vRec.x, vRec.y);
hsb.setSize(vRec.width, vRec.height);
}
}

public class GUI extends JFrame /* implements ActionListener */
{

// GUI objects
JTextArea m_pText;
JScrollPane m_pScrollPane;

GUI()
{
super("Test GUI");

m_pText = new JTextArea("This is a test.");
m_pText.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));

m_pScrollPane = new JScrollPane(m_pText);


m_pScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

m_pScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

m_pScrollPane.setLayout(new MySPLayout());

add(m_pScrollPane);

pack();
}

public void dispose()
{
super.dispose();
}
}
 
P

pek

You'll have to override the layout manager.

In the example below, I made the scroll pane display 2 vertical bars.
All I did was resize/reposition the viewport, the vertical and the
horizontal bar.

The outer most vertical bar will still act like that standard horiz
bar.
The inner vertical bar will be the same normal vertical bar.

PS.
Are you sure you really need to do this?
I agree with RGB that you sure have a look at the other default panes
just make sure. =/

---------------------------------------------------------

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class MySPLayout extends ScrollPaneLayout
{
public void layoutContainer(Container parent)
{
hsb.setOrientation(JScrollBar.HORIZONTAL);
super.layoutContainer(parent);

Rectangle hRec = hsb.getBounds();
Rectangle vRec = vsb.getBounds();
Dimension size = viewport.getExtentSize();

// adjust viewport size, add to the bottom, sub from right side
size.width -= vRec.width;
size.height += hRec.height;
viewport.setExtentSize(size);

// get rid of the corner
vRec.height += hRec.height;

// scoot over to make room
vsb.setLocation(vRec.x - vRec.width, vRec.y);
vsb.setSize(vRec.width, vRec.height);

hsb.setOrientation(JScrollBar.VERTICAL);
hsb.setLocation(vRec.x, vRec.y);
hsb.setSize(vRec.width, vRec.height);
}

}

public class GUI extends JFrame /* implements ActionListener */
{

// GUI objects
JTextArea m_pText;
JScrollPane m_pScrollPane;

GUI()
{
super("Test GUI");

m_pText = new JTextArea("This is a test.");
m_pText.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));

m_pScrollPane = new JScrollPane(m_pText);

m_pScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

m_pScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

m_pScrollPane.setLayout(new MySPLayout());

add(m_pScrollPane);

pack();
}

public void dispose()
{
super.dispose();
}

}

First of all thank you for your replies. To be fair, I'll explain a
little more. I want to create a panel that, when needed, will display
vertical and horizontal bars. When either a horizontal or a vertical
scroll bar appears, I want them to be double (2 horizontal scroll bars
or 2 vertical scroll bars). Both will point at the same view, but each
will behave different (probably one of them will scroll faster or
scroll the other way or anything). I want to explicitly define how
each will behave.

I'll try your code and report back. Thank you once again.
 
P

pek

First of all thank you for your replies. To be fair, I'll explain a
little more. I want to create a panel that, when needed, will display
vertical and horizontal bars. When either a horizontal or a vertical
scroll bar appears, I want them to be double (2 horizontal scroll bars
or 2 vertical scroll bars). Both will point at the same view, but each
will behave different (probably one of them will scroll faster or
scroll the other way or anything). I want to explicitly define how
each will behave.

I'll try your code and report back. Thank you once again.

OK.. I must admit, the code was awesome! I didn't imagine what it
would do! Thank you very much.

So, since I don't know how (or even why) this code works, I'll just
post what exactly I want to do and hope that you'll also help me on
this.

I came across Picassa this other day. I'm not actually sure if it was
Picassa, but anyway. I noticed that the vertical scroll bar isn't the
conventional one. It works like this:

Let's say that the viewports' view is three screens high.
The vertical bars' knob is at the middle.
If you drag it either way (up or down) the view scrolls respectively.
The cool the about it is that the further the knob is away from the
starting middle point, the faster the view scrolls.
When the knob isn't anymore dragged (you release the mouse) the knob
returns to it's initial position (the middle).

What I'm trying to accomplish (with no luck what so ever) is have both
scroll bars (conventional and picassa-like) in one pane using code
like JDoubleScrollPane.setPicassaLikeVerticalScrollBarPolicy(...) (OK,
it's rather huge, but I'll a better one). So, the developers and the
end user would have the best of both worlds.

Now, I didn't know where to start. I actually thought I had to create
a new component and implement its' own look and feel. Which goes to
say what is the level of my knowledge in Swing.

So, what do you think? Can you help? Or at least give me some pointers
and hints?

Thank you very much.

Panagiotis.
 
J

Jeff Higgins

pek said:
Let's say that the viewports' view is three screens high.
The vertical bars' knob is at the middle.
If you drag it either way (up or down) the view scrolls respectively.
The cool the about it is that the further the knob is away from the
starting middle point, the faster the view scrolls.
When the knob isn't anymore dragged (you release the mouse) the knob
returns to it's initial position (the middle).

What I'm trying to accomplish (with no luck what so ever) is have both
scroll bars (conventional and picassa-like) in one pane using code
like JDoubleScrollPane.setPicassaLikeVerticalScrollBarPolicy(...) (OK,
it's rather huge, but I'll a better one). So, the developers and the
end user would have the best of both worlds.

So, what do you think? Can you help? Or at least give me some pointers
and hints?

Some applications use the mouse wheel for that behavior.
Adobe Acrobat Reader comes to mind. When you press the
mouse wheel the cursor is changed to indicate a multi-
directional pan control that pans at a rate that increases
with the distance the mouse moves away from the original
drag point. It's not self-centering. The scrollbar knobs
still indicate the position within the document.
 
D

David A. Redick

OK.. I must admit, the code was awesome! I didn't imagine what it
would do! Thank you very much.

No prob.
So, since I don't know how (or even why) this code works, I'll just
post what exactly I want to do and hope that you'll also help me on
this.

The how and why are fairly easy once you start thinking in the Java
way (or OO way).
Think of JScrollPane as just a container for 2 scrollbars and a view.
How does it know where to put things? Simple, the ScrollPaneLayout
object
tells it.

Every panel/pane type object uses a Layout object to place things.
Some are easily hijacked (like JScrollPane) some are harder (JTable
seems to be near impossible).

In the example I used earlier I just shuffled stuff around and resized
them
every time the JScrollPane wanted an update.

See
http://en.wikipedia.org/wiki/Model-view-controller

The model can be thought of as the actual data interface (JTextArea).
The view can be thought of as the thing that controls what you see
(ScrollPaneLayout).
The controller can be thought of as the thing that handles events
(JScrollPane).

Of course the boundaries are rather fuzzy.

If you zoom in on the scroll bars then what does what is a bit
different.
Model: BoundedRangeModel (which is inside JScrollBar)
View: JScrollBar (I'm guessing via BoxLayout. Docs don't say)
Controller: JScrollBar
[snip]

Now, I didn't know where to start. I actually thought I had to create
a new component and implement its' own look and feel. Which goes to
say what is the level of my knowledge in Swing.

You should be careful with your choice of words.
People used look and "feel to mean" a wide variety of concepts.
In the Java world look and feel just means graphical look (see PLAF).

I prefer to break the GUI into three parts.
Look -
the graphical side (does a button have the 3d look or flat?
Is there a drop shadow? Does a button have rounded edges or
hard corners.
Is this a red or gray or blue button. Does it have hash
marks?

Behavior (what non-programmers usually mean by feel) -
How does scroll bar act?
User interaction with widgets.

Layout -
Where stuff is placed on the screen, the size of that stuff.

Naturally there is some overlap.
And this is really just meant as an aside.
A FYI, so to speak. Take with liberal amounts of salt.

To put this in the same context as my reply above...

Look is controlled by the UIManager object and ScrollBarUI,
ScrollPaneUI, TextUI (all behind the scenes).
Behavior is controlled by the JScrollPane, JScrollBars and whatever
the JViewport is.
Layout is controlled by ScrollPaneLayout.
So, what do you think? Can you help? Or at least give me some pointers
and hints?

Well below is a basic template for DoubleScollPane.
The inner scrollbars are the standard ones. The outer ones will be
the wacky Picassa ones.
At the moment they just sit there and don't do a damn thing.

I'm still not 100% sure of how this Picassa scroll bars will behave so
its
difficult to say on how to implement them.

You'll probablity have to make a PicassaScrollBar object that extends
ScrollBar.

Override several of the methods (maximum, minimum, BlockIncrement,
UnitIncrement, value, IsAdjusting, VisibleAmount)
and fittle with BoundedRangeModel.

Make DoubleScrollPane an AdjustmentListener for the two
PicassaScrollBars.
And adjust the view port in either the pane itself or the layout.
To be honest, I'm not sure which is the more proper. I can see it for
either one but
I'm really not sure. I'd guess the pane.

Here's my guess of the Picassa model:

MIN*-----A------B------C-----*MAX

When the knob is at B it has a scroll speed of 0
When the knob is at A it has a scroll speed of -x
When the knob is at C it has a scroll speed of x

So you'll have to figure out how to compute the speed based on the
position (fittle with BoundedRangeModel)

Heres the DoubleScrollPane template (its much cleaner than my original
example):

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class DoubleScrollPaneLayout extends ScrollPaneLayout
{
protected JScrollBar m_pHSB2;
protected JScrollBar m_pVSB2;

DoubleScrollPaneLayout(JScrollBar pHSB2, JScrollBar pVSB2)
{
super();

m_pHSB2 = pHSB2;
m_pVSB2 = pVSB2;
}

public void layoutContainer(Container parent)
{
super.layoutContainer(parent);

Rectangle hRec = hsb.getBounds();
Rectangle vRec = vsb.getBounds();
Dimension size = viewport.getExtentSize();

// make viewport size a bit smaller
size.width -= vRec.width;
size.height -= hRec.height;
viewport.setExtentSize(size);

// scoot VSB1 over to the left
vsb.setLocation(vRec.x - vRec.width, vRec.y);

// scoot HSB1 up
hsb.setLocation(hRec.x, hRec.y - hRec.height);

// adjust size to make the corner
hRec.width -= vRec.width;
vRec.height -= hRec.height;
hsb.setSize(hRec.width, hRec.height);
vsb.setSize(vRec.width, vRec.height);

// set our second set of scroll bars to be
// the same size as its partner
m_pHSB2.setSize(hRec.width, hRec.height);
m_pVSB2.setSize(vRec.width, vRec.height);

// set the location of our second set of scroll bars
m_pHSB2.setLocation(hRec.x, hRec.y);
m_pVSB2.setLocation(vRec.x, vRec.y);
}
}

// 2 horiz. bars and 2 vert. bars.
class DoubleScrollPane extends JScrollPane
{
protected JScrollBar m_pHSB2;
protected JScrollBar m_pVSB2;

DoubleScrollPane(Component pView)
{
super(pView);


setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

m_pHSB2 = new JScrollBar(JScrollBar.HORIZONTAL);
m_pVSB2 = new JScrollBar(JScrollBar.VERTICAL);

m_pHSB2.setVisible(true);
m_pVSB2.setVisible(true);

add(m_pHSB2);
add(m_pVSB2);

setLayout(new DoubleScrollPaneLayout(m_pHSB2, m_pVSB2));
}
}


public class GUI extends JFrame
{
// GUI objects
JTextArea m_pText;
DoubleScrollPane m_pScrollPane;

public GUI()
{
super("Test GUI");

m_pText = new JTextArea("This is a test.");
m_pText.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));

m_pScrollPane = new DoubleScrollPane(m_pText);

add(m_pScrollPane);
pack();
}

public static void main(String[] args)
{
GUI pApp = new GUI();

try
{

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.updateComponentTreeUI(pApp);
}
catch(Exception ex)
{
System.out.println("Java's PLAF is fubar");
}

pApp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pApp.setVisible(true);
}
}
 
D

David A. Redick

I played around some more this morning and I think I figured it out.
Just add the SpringyScrollBar class and replace the DoubleScrollPane
class.
The rest is the same.

class SpringyScrollBar extends JScrollBar
{
public SpringyScrollBar(int iOrientation)
{
super(iOrientation);

setMaximum(99);
setMinimum(0);
moveToMid();
}

public int getMid()
{
int iMid = getMaximum() - getMinimum() + 1;
iMid /= 2;

int iSize = getVisibleAmount()/2;

iMid -= iSize;

return iMid;
}

public void moveToMid()
{
setValue(getMid());
}

public int getSpeed()
{
int i = getValue();
i -= getMid();

if(i < 0)
i *= -1;

return i;
}

public int getDir()
{
int i = getValue();
i -= getMid();

if(i < 0)
return -1;

return 1;
}
}




// 2 horiz. bars and 2 vert. bars.
class DoubleScrollPane extends JScrollPane implements
AdjustmentListener
{
protected SpringyScrollBar m_pHSB2;
protected SpringyScrollBar m_pVSB2;

DoubleScrollPane(Component pView)
{
super(pView);


setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

m_pHSB2 = new SpringyScrollBar(JScrollBar.HORIZONTAL);
m_pVSB2 = new SpringyScrollBar(JScrollBar.VERTICAL);

m_pHSB2.addAdjustmentListener(this);
m_pVSB2.addAdjustmentListener(this);

m_pHSB2.setVisible(true);
m_pVSB2.setVisible(true);

add(m_pHSB2);
add(m_pVSB2);

setLayout(new DoubleScrollPaneLayout(m_pHSB2, m_pVSB2));
}

public void adjustmentValueChanged(AdjustmentEvent e)
{
SpringyScrollBar p = (SpringyScrollBar) e.getAdjustable();
boolean bIsHoriz = p.equals(m_pHSB2);
int iSpeed = p.getSpeed();
int iDir = p.getDir();

// Adjust position.
JScrollBar pBar;
if(bIsHoriz)
pBar = horizontalScrollBar;
else
pBar = verticalScrollBar;


int iInc = pBar.getUnitIncrement(iDir);
iInc *= iSpeed;
int iValue = pBar.getValue();
iValue += iDir*iInc;
pBar.setValue(iValue);


if(!e.getValueIsAdjusting())
{
// move back to center
p.moveToMid();
}
}
}
 
P

pek

I played around some more this morning and I think I figured it out.
Just add the SpringyScrollBar class and replace the DoubleScrollPane
class.
The rest is the same.

class SpringyScrollBar extends JScrollBar
{
public SpringyScrollBar(int iOrientation)
{
super(iOrientation);

setMaximum(99);
setMinimum(0);
moveToMid();
}

public int getMid()
{
int iMid = getMaximum() - getMinimum() + 1;
iMid /= 2;

int iSize = getVisibleAmount()/2;

iMid -= iSize;

return iMid;
}

public void moveToMid()
{
setValue(getMid());
}

public int getSpeed()
{
int i = getValue();
i -= getMid();

if(i < 0)
i *= -1;

return i;
}

public int getDir()
{
int i = getValue();
i -= getMid();

if(i < 0)
return -1;

return 1;
}

}

// 2 horiz. bars and 2 vert. bars.
class DoubleScrollPane extends JScrollPane implements
AdjustmentListener
{
protected SpringyScrollBar m_pHSB2;
protected SpringyScrollBar m_pVSB2;

DoubleScrollPane(Component pView)
{
super(pView);

setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

m_pHSB2 = new SpringyScrollBar(JScrollBar.HORIZONTAL);
m_pVSB2 = new SpringyScrollBar(JScrollBar.VERTICAL);

m_pHSB2.addAdjustmentListener(this);
m_pVSB2.addAdjustmentListener(this);

m_pHSB2.setVisible(true);
m_pVSB2.setVisible(true);

add(m_pHSB2);
add(m_pVSB2);

setLayout(new DoubleScrollPaneLayout(m_pHSB2, m_pVSB2));
}

public void adjustmentValueChanged(AdjustmentEvent e)
{
SpringyScrollBar p = (SpringyScrollBar) e.getAdjustable();
boolean bIsHoriz = p.equals(m_pHSB2);
int iSpeed = p.getSpeed();
int iDir = p.getDir();

// Adjust position.
JScrollBar pBar;
if(bIsHoriz)
pBar = horizontalScrollBar;
else
pBar = verticalScrollBar;

int iInc = pBar.getUnitIncrement(iDir);
iInc *= iSpeed;
int iValue = pBar.getValue();
iValue += iDir*iInc;
pBar.setValue(iValue);

if(!e.getValueIsAdjusting())
{
// move back to center
p.moveToMid();
}
}

}

Amazing! ;) Exactly how I wanted it! I can't thank you enough. I'll
tweak some things and report back. Oh. In case you have any website/
blog, I'd like to hear about it. I want to post the source code in my
blog and have some kind of reference to you.. ;)

By the way, there is a little problem. I'm running Linux, and when the
knob is being dragged, it moves the view perfectly. But if let's say,
I drag the knob from the middle to 1/3 position and just hold the knob
there, the view doesn't move. As long as the knob is being dragged it
does. I hope you understand what I'm saying.

I tried adding MouseMotionListener and MouseListener but again, when
the knob is still, no event is fired, no matter in which position it
is.

Other than that, excellent! ;)
 
R

Roedy Green

Is there any way I can customize a JScrollPane and instead of one
horizontal JScrollPane it would show two? Can you point me somewhere?

It strikes me as an odd thing to do given how precious screen real
estate is.

I would study the code for JScrollPane and related classes in src.zip.
You could then base your class overriding just what you need, or
building one from scratch without any special features.
 
P

pek

Something like this should work:

private Timer horizTimer = new Timer(250,
new ActionListener() {
public void actionPerformed(ActionEvent ev) {
int iDir = m_pHSB2.getDir();
int iInc = horizontalScrollBar.getUnitIncrement(iDir);
int iSpeed = m_pHSB2.getSpeed();
iInc *= iSpeed;
int iValue = horizontalScrollBar.getValue();
iValue += iDir*iInc;
horizontalScrollBar.setValue(iValue);
}
});

private Timer vertTimer = new Timer(250,
new ActionListener() {
public void actionPerformed(ActionEvent ev) {
int iDir = m_pVSB2.getDir();
int iInc = verticalScrollBar.getUnitIncrement(iDir);
int iSpeed = m_pVSB2.getSpeed();
iInc *= iSpeed;
int iValue = verticalScrollBar.getValue();
iValue += iDir*iInc;
verticalScrollBar.setValue(iValue);
}
});

public void adjustmentValueChanged(final AdjustmentEvent e)
{
SpringyScrollBar p = (SpringyScrollBar) e.getAdjustable();
boolean bIsHoriz = p.equals(m_pHSB2);
Timer timer = bIsHoriz? horizTimer : vertTimer;
if(!e.getValueIsAdjusting())
{
// move back to center
timer.stop();
p.moveToMid();
}
else {
if (p.getSpeed() != 0 && !timer.isRunning()) {
timer.start();
}
}
}

Thanks to this code and the one provided by the others, I finally
created exactly what I wanted. I did some changes (such as setting
speed, enable/disable springyscrollbar etc.) and I'm ready to post it
but there is one small problem. I'm trying to follow as much
"principles" as the JScrollPane has, so, I didn't forced
verticalScrollBarPolicy to ALWAYS but left it for the user to decide.
Well, the default vertical and horizontal scrollbars have each a
policy of their own. What about the springyscrollbars? How do I
implement a policy? The source code of JScrollPane doesn't indicate
anywhere where the policies are used, or more likely, *I* didn't find
any such indication. Any suggestions on how to implement policies for
the springyscrollbars?

Thank you again.
 
J

Jeff Higgins

pek said:
Well, the default vertical and horizontal scrollbars have each a
policy of their own. What about the springyscrollbars? How do I
implement a policy? The source code of JScrollPane doesn't indicate
anywhere where the policies are used, or more likely, *I* didn't find
any such indication. Any suggestions on how to implement policies for
the springyscrollbars?

int HSSBPolicy
int VSSBPolicy

get/set SSBPolicies

DoubleScrollPaneLayout
 
P

pek

int HSSBPolicy
int VSSBPolicy

get/set SSBPolicies

DoubleScrollPaneLayout

Nope, nothing worked. I used the same JScrollPaneConstants for the
policy in the DoubleScrollPane, but nothing changed. I copied the code
that JScrollPane uses from the source code. It is almost the same
thing, but nothing happens. But when I set the
horizontalScrollBarPolicy (not the springy one) it works for both!
Probably because I use the same JScrollPaneConstants, but then again,
how else can I implement this?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top