Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (539)
Games in Android Showcase (133)
games submitted by our members
Games in WIP (603)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1] 2
  ignore  |  Print  
  Making A Slider Component Problem  (Read 3147 times)
0 Members and 1 Guest are viewing this topic.
Offline Dicee

Junior Devvie


Medals: 2



« Posted 2013-12-04 15:02:29 »

I'm creating a slider component for my game with graphics in Slick2D. Almost everything is working as supposed but there's a problem, to properly control the slider box. As you can see below in the code, I'm increasing or decreasing the value, instead of changing the sliderBoxX by the position of the mouseX. So tell me should I start all over again or is it still possible to make this code working in this code order, to make sliderBoxX and component values change by mouse X coordinate?
As a matter of a problem, is the order of the code where I compute and check things wrong?

Code is inside update method:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
this.minusBoxX = this.x;
this.minusBoxY = this.y;
this.minusBoxWidth = this.height;
this.minusBoxHeight = this.height;

this.plusBoxX = this.x + this.width - this.height;
this.plusBoxY = this.y;
this.plusBoxWidth = this.height;
this.plusBoxHeight = this.height;
       
this.sliderBoxWidth = this.height / 2;
this.sliderBoxHeight = this.height / 2;

this.minSliderX = this.minusBoxX + this.minusBoxWidth;
this.maxSliderX = this.plusBoxX;
this.lineWidth = Math.abs(minSliderX - maxSliderX) - this.sliderBoxWidth;

this.percentage = this.value / this.maxValue;

this.sliderBoxX = this.minSliderX + (this.lineWidth * this.percentage);
this.sliderBoxY = this.y + ((this.y + this.height) - this.y) / 2
                         - (this.sliderBoxHeight / 2);

float minSliderBoxX = this.sliderBoxX;
float minSliderBoxY = this.sliderBoxY;
float maxSliderBoxX = this.sliderBoxX + this.sliderBoxWidth;
float maxSliderBoxY = this.sliderBoxY + this.sliderBoxHeight;
if ((mx > minSliderBoxX && mx < maxSliderBoxX) && (my > minSliderBoxY && my < maxSliderBoxY)) {
    if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
        if (this.percentage * 100 >= 0 && this.percentage * 100 <= 100) {
            this.wasSliderBoxDown = true;
        }
    }
}
if (this.wasSliderBoxDown) {
    if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
        if (this.percentage * 100 >= 0 && this.percentage * 100 <= 100) {
            // this.sliderBoxX = mx; something like this
            if (mx > maxSliderBoxX) {
                this.value += 0.1f * 100.0f; // this works, but is not correct
            }
            if (mx < minSliderBoxX) {
                this.value -= 0.1f * 100.0f; // this works, but is not correct
            }
        }
    } else {
        this.wasSliderBoxDown = false;
    }
}




Edit:
Ok, I'm changing mind. I will make first the slider line made of a component width, and not like I did between minus and plus boxes. But now the questions is, what should I change current value or a slider box position from the mx and then compute percentage? And what checks should I provide, like should I check the percentage, value or current mx positon for bounds?
Offline Troubleshoots

JGO Knight


Medals: 36
Exp: 7-9 months


Damn maths.


« Reply #1 - Posted 2013-12-04 15:18:03 »

I've not tried anything like this before but I'd assume you would have to:
  • Calculate the distance of 1% of the slider width
  • When the slider is clicked, calculate where it is clicked
  • Calculate the X-distance that the mouse has been moved and round it down to the nearest 1%
  • Redraw the slider and update as you wish

Why are all OpenGL tutorials written in Brainf**k?
Offline Dicee

Junior Devvie


Medals: 2



« Reply #2 - Posted 2013-12-04 15:33:05 »

Alright, thanks on the suggestion. I will take a deep look at it, after I back home.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #3 - Posted 2013-12-04 18:31:52 »

I'll have to make some assumptions about the rest of your class, so forgive me if I'm incorrect in some aspects. My first comment is that when designing the basic controls, keep them as simple and focused as possible. The plus and minus buttons shouldn't be part of a base slider class IMHO. They can always be "attached" to a basic slider control via events further up the abstraction chain. As it is, they make the calculations for the slider more complex than they needs to be. (Technically the drag box could be separated out as well, but we'll leave it for now since it doesn't complicate things too much.)

Lets assume your slider control consists of 2 items, a drag box, and a line that represents the extents that the drag box can be moved. The code would be something like this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
       
/*
 * Technically the 5 variables below shouldn't be calculated every
 * update. They should be computed when the control is initialized
 * and only recalculated if the control is moved/resized or if
 * their dimensions are altered through setter methods.
 */

this.sliderBoxWidth = this.height >> 1;
this.sliderBoxHeight = this.height >> 1;
this.sliderBoxY = this.y;
this.lineWidth = this.width - this.sliderBoxWidth;
/*
 * I'm guessing you have a variable similar to this in your class
 * even though it's not listed in your snippet.
 */

this.lineX = this.x + (this.sliderBoxWidth>>1);

// Assume that the control isn't pressed by default.
this.wasSliderBoxDown = false;

// Compute the mouse position in relation to the x position of the drag line.
int mx_relative = mx - lineX;

/*
 * Check to make sure that the mouse is being pressed and that its relative
 * position is within our drag line bounds.
 */

if(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) && mx_relative >= 0 && mx_relative <= this.lineWidth) {
    // Do the value/offset computations if the mouse is in bounds.
    this.percentage = (float)(mx_relative/this.lineWidth);
    this.value = Math.round(this.percentage * 100);
    // The following will center the drag box on the mouse's position.
    this.sliderBoxX = mx_relative + this.lineX - (sliderBoxWidth>>1);
    // Indicate that the control is being pressed.
    this.wasSliderBoxDown = true;
}

You'll have to excuse any minor errors in the above code; I didn't have time to test it in my IDE, but I'm pretty sure the calculations should work. Hope this helps. Cool

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #4 - Posted 2013-12-04 22:00:09 »

Thank you on the constructive answer, I very appreciate it.
However, this is my first time creating GUI components, also my first serious game project, using some library.
By the way, it's nice to see that you're using shift operators, for which I've never had a chance to use them before nor to see them in any other code. So, I'd be thankful if you could tell me what this.height >> 1 does? Does it checks if the current height value was changed for 1 ?
Anyway, I'm really tired now, and I have to wake up early in a morning, so I will probably continue programming tommorow tonight and try out your solution.
I will reply if it worked.
All best!
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #5 - Posted 2013-12-04 22:35:45 »

By the way, it's nice to see that you're using shift operators, for which I've never had a chance to use them before nor to see them in any other code. So, I'd be thankful if you could tell me what this.height >> 1 does? Does it checks if the current height value was changed for 1 ?

Ah, sorry about that. I'm so used to using bit shifts that I sometimes forget not everybody is familiar with them. the ">>1" essentially divides a number in half and discards the remainder. It all comes down to the way binary representations of numbers work. If you're unfamiliar with binary operations, I'd suggest doing some research into them sometime. It's definitely not a "drop everything and learn it now" type of deal, but the concepts can be quite useful and you'll find them in most languages. Smiley

Slick2D has it's own way of doing certain tasks, but it's not all that different from using vanilla Java in most aspects. I'm pretty much doing what you're doing at the moment and building a set of controls for use in my engine. It seems a bit overwhelming at first, but once you get into it and start breaking down how things work, it's not too bad for the most part. Best of luck, and keep us updated on your progress. Cool


Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #6 - Posted 2013-12-10 20:57:10 »

Hello again! It's been a week since last time we talk, I said I will continue tommorow night, but some other plans came out...
So you say that shift operator ">>", divides a number in half and discards the remainder, which I do understand now,
but, you also say in your comment above this code,

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
/* 
 * Technically the 5 variables below shouldn't be calculated every
 * update. They should be computed when the control is initialized
 * and only recalculated if the control is moved/resized or if
 * their dimensions are altered through setter methods.
 */

this.sliderBoxWidth = this.height >> 1;
this.sliderBoxHeight = this.height >> 1;
this.sliderBoxY = this.y;
this.lineWidth = this.width - this.sliderBoxWidth;


that this variables should be computed when the control is initialized and only recalculated if the control is moved/resized.
S, how this shift operator checks as well if a value is different (moved/resized), as you did not use an if condition,
or it was just your suggestion to me, in order to tell me how things should be done?

Anyway, let's back to the topic.
Everything works as intended, but the only problem now is I'm not getting a proper percentage and a value.

If i write,
1  
2  
this.percentage = (mxRelative / this.lineWidth) * 100.0f;
this.value = this.percentage / 100.0f * this.maxValue;

and I go maximum with slider box, this will return:
percentage: 99.775276
value: 997.7528
Which is not a 100%, and not 1000.00

I can round percentage, and it will go to 100 for percentage, and 1000.0 for value
1  
2  
this.percentage = Math.round((mxRelative / this.lineWidth) * 100.0f);
this.value = this.percentage / 100.0f * this.maxValue;

but that's not what I need, I need a precise number, like 957.50
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #7 - Posted 2013-12-10 22:04:34 »

how this shift operator checks as well if a value is different (moved/re-sized), as you did not use an if condition,
or it was just your suggestion to me, in order to tell me how things should be done?

I guess I could have been a bit more clear. Smiley I meant that ideally, those calculations should be moved outside of the update method and into their own "calculate dimensions" method so they weren't called each time the slider was updated. Instead you would call the "calculate dimensions" function from any methods that changed the dimensions of the control, for example any set size/dimension method, or anything method which set paddings or border widths on the control. This way the calculations are only done when needed.

For example:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
public class MySliderClass {
    private int sliderBoxWidth;
    private int sliderBoxHeigt;
    private int lineWidth;
    private int borderSize;
    ...

    protected void calculateDimensions() {
        this.sliderBoxWidth = this.height >> 1;
        this.sliderBoxHeight = this.height >> 1;
        this.sliderBoxY = this.y;
        this.lineWidth = this.width - this.sliderBoxWidth;
    }

    public void setControlSize(int w, int h) {
        ...
        calculateDimensions();
        ...
    }

    public void setBorderSize(int size) {
        ...
        calculateDimensions();
        ...
    }

    public void update() {
        // No need to do the calculations here. They're already done for
        // us by the other methods on an as needed basis.
    }
}

After replying to your topic, I went through the exercise of implementing my own slider to make sure I hadn't steered you in a complete wrong direction, and can say with some certainty that you should be fine leaving those calculations within the update function. Wink It was more of a possible optimization suggestion than a must do. In the end, for simplicity sake, I left my calculations in the render function for now and it works fine for me; in all honesty I probably will go back at some point soon and refactor it to conform to my own advice. One other note, after further consideration, the sliderBoxY "calculation" should probably stay within the update method since the other calculations don't rely on it.

and I go maximum with slider box, this will return:
percentage: 99.775276
value: 997.7528
Which is not a 100%, and not 1000.00

Interesting. Huh If I had to take a guess, maybe you're forgetting to account for overhang of the slider when you compute mxRelative. This would make the slider think it's slightly to the left of where it appears to be. If you're clamping your values, then you wouldn't notice the discrepancy when the slider was all the way to the left since its value would be less than 0, the clamping would force it up to 0. At the rightmost position the clamping wouldn't be in effect since you're value is still less than 1.0f so it would be apparent there.

If you're clamping your values, do a test with the clamping disabled and see if the minimum value returned by the slider is less than 0. If it is, then recheck your mxRelative calculation. If it's at exactly 0.0f without clamping, then try posting the relevant sections of your updated class and maybe I can spot what's going awry.

If it helps, here is the source to the quick slider implementation I threw together. It's rough in spots and uncommented, but it works. I've highlighted the line where I'm calculating the relative mouse position to give you an idea of what I'm talking about with including the overhang. If you have any questions about it, just let me know and I'll do my best to answer. One last note, the "computeRenderingOffset()" function returns a Point that represents the control's absolute position on the screen. Since my control layout is based on relative position to a parent control, the controls x/y position is never used directly.

Sorry for the long winded reply. HTH. Cool

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #8 - Posted 2013-12-10 22:55:18 »

Well, this is the only component so far with which I'm stuck, and I think I know where's the problem,  but right now I just can't throw it out of my head.
Anyway, I'm going to call now a sleep() method on self, it will help me definitely.. and I believe I will find out the solution if not and more. Also, it is really intersting that you name your class HSlider, I call it PSlider, the reason is, I'm making a Poker game, just like the one on the facebook, and I'm not going to talk about how big differences are between your component design and my Cheesy. But I still like the way I created it, even tho it only contains one AbstractComponent, it still does some job and cuts a lot of the trash code from the component in general.
Hopefully, I will learn this semester pretty well UML diagrams, so I can start some serious projects and give focus more on implementation and less coding.
Thanks again and gn!
Offline ags1

JGO Wizard


Medals: 75
Projects: 3
Exp: 5 years


Make code not war!


« Reply #9 - Posted 2013-12-10 23:08:58 »

No! Not UML... UML is banned in many workplaces, causes arguments, drives people crazy :-)

But if you want to play with quick UML diagrams:

http://yuml.me/diagram/scruffy/class/draw

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #10 - Posted 2013-12-11 00:28:27 »

Also, it is really intersting that you name your class HSlider

Ah, don't put too much thought into that one. The "H" is just for horizontal. I still haven't decided whether I will make a "V" slider as well, or if I'm just going to add some sort of orientation logic into the mix and just end up with a Slider component. At the moment their being implemented for a map editor I'm writing so I've aimed more for functional than beautiful when it comes to how things are implemented. I'm sure I'll tweak them a lot more before having them ready for final product. A poker game sounds like a fun project.

I'm not going to talk about how big differences are between your component design and my Cheesy. But I still like the way I created it, even tho it only contains one AbstractComponent, it still does some job and cuts a lot of the trash code from the component in general.

Nothing wrong with going about things a different way. For better or worse, a lot of my GUI control design is influenced by the way Swing does things; that's what I'm most familiar/comfortable with. So long as a solution satisfactorily addresses a given problem, it's a good one. Grin As a note, the controls I have tend to be fairly light weight. The control manager that glues them all together is a different story. LOL

Hopefully, I will learn this semester pretty well UML diagrams, so I can start some serious projects and give focus more on implementation and less coding.
Thanks again and gn!

I'm more of a "git er' done" type of coder. I've messed around with graphing out program flow, but at the end of the day I get a better understanding of a problem through experimentation rather than theorization. Different strokes for different folks though. Wink Have a good night, and let us know how your project progresses. Cool

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #11 - Posted 2013-12-11 19:03:32 »

Quote
No! Not UML... UML is banned in many workplaces, causes arguments, drives people crazy :-)

But if you want to play with quick UML diagrams:
I forgot to mention, I'm not going to learn UML for fun, that's just my task, because it will be on my exam, unfortunately ...

CodeHead, I just gave up on trying to fix my slider component with those +/- boxes on the left and right side.
So I started following your instructions, with first code, but I ended up with the same problem as with +/- box I had, so incorrect value and percentage.

This is how the slider looks now, also you can see the maximum value in text field, which shows the value(0-1000)/percentage.



p.s. I know, many things are not done properly here, for example using the listener interfaces, instead I was doing everything in actionPerform a.k.a. update method..
But I will take care of that later..

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
public class PSlider extends PAbstractComponent {

    private float value;
    private float percentage;
    private float minValue, maxValue;
   
    private float lineWidth, lineHeight, lineX, lineY;
    private float sliderBoxWidth, sliderBoxHeight, sliderBoxX, sliderBoxY;
   
    private boolean wasSliderBoxDown = false;
           
    private float minusBoxWidth, minusBoxHeight, minusBoxX, minusBoxY;
    private float plusBoxWidth, plusBoxHeight, plusBoxX, plusBoxY;
   
    private PLabel label;
   
    public PSlider(float width, float height) throws SlickException {
        super();
       
        this.width = width;
        this.height = height;
       
        this.label = new PLabel(PFontType.LucidiaFaxRegular, 26, false, false);
        this.label.setNormalColor(new Color(0, 0, 0, .75f));
       
        this.value = 0;
    }
   
    public PSlider(float width, float height, float currentValue) throws SlickException {
        this(width, height);
       
        this.value = currentValue;
    }

    @Override
    public void render(Graphics g, float x, float y) throws SlickException {
        this.x = x;
        this.y = y;
       
        // render rect and fill
        float cornerRadius = 0;
        g.setColor(new Color(0, 0, 0, .5f));
        g.fillRoundRect(x, y, width, height, (int) cornerRadius);
        g.setColor(Color.decode("#ffffbe"));
        g.drawRoundRect(x, y, width, height, (int) cornerRadius);
       
        // render line
        float lineX1 = this.lineX;
        float lineX2 = this.lineX + this.lineWidth;
        float lineY1 = this.lineY;
        float lineY2 = lineY1;
        g.setColor(Color.decode("#ffffbe"));
        g.drawLine(lineX1, lineY1, lineX2, lineY2);
       
        // render slider box
        float sBoxX = this.sliderBoxX;
        float sBoxY = this.sliderBoxY;
        float sBoxWidth = this.sliderBoxWidth;
        float sBoxHeight = this.sliderBoxHeight;
        g.setColor(new Color(223, 182, 105));
        g.fillRoundRect(sBoxX, sBoxY, sBoxWidth, sBoxHeight, (int) cornerRadius);
        g.setColor(Color.decode("#ffffbe"));
        g.drawRoundRect(sBoxX, sBoxY, sBoxWidth, sBoxHeight, (int) cornerRadius);
    }
   
    public void performAction(GameContainer gc, int mx, int my) throws SlickException {
        Input input = gc.getInput();

        this.sliderBoxWidth = this.height / 2;
        this.sliderBoxHeight = this.height / 2;
        this.sliderBoxY = this.y + ((this.y + this.height) - this.y) / 2
                                 - (this.sliderBoxHeight / 2);
       
        this.lineWidth = this.width - this.sliderBoxWidth;
        this.lineHeight = 0;
        this.lineX = this.x + (this.sliderBoxWidth / 2);
        this.lineY = this.y + ((this.y + this.height) - this.y) / 2;
       
        float mxRelative = mx - this.lineX;

        if ((this.sliderBoxX < this.lineX) || (this.sliderBoxX > this.lineX + this.lineWidth)) {
            this.sliderBoxX = this.lineX;
        }
       
        if ((my > this.y) && my < this.y + this.height) {
            if (input.isMousePressed(Input.MOUSE_LEFT_BUTTON)) {
                if ((mxRelative >= 0) && (mxRelative <= this.lineWidth)) {
                    this.percentage = (float) (mxRelative / this.lineWidth);
                    this.value = Math.round(this.percentage * 100);
                    this.sliderBoxX = mxRelative + this.lineX - (sliderBoxWidth / 2);
                }
            }
        }
       
        float minSliderBoxX = this.sliderBoxX;
        float minSliderBoxY = this.sliderBoxY;
        float maxSliderBoxX = this.sliderBoxX + this.sliderBoxWidth;
        float maxSliderBoxY = this.sliderBoxY + this.sliderBoxHeight;
        if ((mx > minSliderBoxX && mx < maxSliderBoxX) && (my > minSliderBoxY && my < maxSliderBoxY)) {
            if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
                this.wasSliderBoxDown = true;
            }
        }
       
        if (this.wasSliderBoxDown) {
            if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
                if (mxRelative >= 0 && mxRelative <= this.lineWidth) {
                    this.percentage = (float) (mxRelative / this.lineWidth);
                    this.value = Math.round(this.percentage * 100);
                    this.sliderBoxX = mxRelative + this.lineX - (sliderBoxWidth / 2);
                }
            } else {
                this.wasSliderBoxDown = false;
            }
        }
    }
   
    public void setInitialValues(float minValue, float maxValue) {
        if (this.minValue != minValue) {
            this.minValue = minValue;
        }
        if (this.maxValue != maxValue) {
            this.maxValue = maxValue;
        }
       
    }
   
    public float getValue() {
        return value;
    }

    public float getPercentage() {
        return percentage;
    }
}
Offline ctomni231

JGO Wizard


Medals: 99
Projects: 1
Exp: 7 years


Not a glitch. Just have a lil' pixelexia...


« Reply #12 - Posted 2013-12-11 20:58:51 »

Here, I felt bad to see you couldn't get it working 100% the way you wanted. I made a rough implementation of a slider with this code. (It is very rough because you have to click on the sliding nub to make it move, a better implementation would be to have it effect the whole bar).

You can copy and paste it straight into a new Java file and you should be able to run it.  I did this for you to get the concept of how a typical slider works. There are no external libraries required to run this...

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class SliderExample extends JComponent implements Runnable, MouseListener, MouseMotionListener{

   private JFrame window;
   private BufferedImage bimg;
   
   //Mouse Position data
   public int mouseX;
   public int mouseY;
   public boolean leftClick;
   public boolean rightClick;
   
   //For setting where to position the slider
   public int offsetX;
   public int offsetY;
   
   //For setting the sliders Position
   public int sliderPos;
   
   //For setting the sliders Length
   public int sliderMax;
   
   public static void main(String args[]){
      SliderExample cool = new SliderExample();
      cool.showWindow();
   }
   
   public void init(){
      offsetX = 100;
      offsetY = 100;
     
      sliderPos = 0;
      sliderMax = 150;
   }
   
   public void updateRender(Graphics2D g){
      ////////////////
      //Logic Portion
      ////////////////
     
      //If left mouse button is clicked and mouse is inside slider
      if(leftClick && mouseX-offsetX > sliderPos-10 && mouseX-offsetX < sliderPos+10 &&
         mouseY-offsetY > 20 && mouseY-offsetY < 40){
         
         //Move the slider to position
         sliderPos = mouseX-offsetX-5;
         
         //Keep the slider within the bounds
         if(sliderPos < 0)
            sliderPos = 0;
         if(sliderPos > sliderMax)
            sliderPos = sliderMax;
      }
     
     
      /////////////////
      //Render Portion
      /////////////////
     
      //The box surrounding the slider
      g.setColor(Color.BLACK);
      g.drawRect(offsetX-30, offsetY-20, 60+sliderMax, 40);
     
      //The slider movement bar
      g.drawRect(offsetX, offsetY-5, sliderMax, 10);
     
      //The slider itself
      g.setColor(Color.white);
      g.fillRect(offsetX+sliderPos-5, offsetY-10, 10, 20);
      g.setColor(Color.black);
      g.drawRect(offsetX+sliderPos-5, offsetY-10, 10, 20);
     
      //Percentage Number
      g.drawString("("+sliderPos+"/"+sliderMax+")", 10, 270);
      g.drawString((((double)sliderPos/sliderMax)*100)+"%", 10, 285);
   }
   
   //////////////////////////////////////////////////////////////////
   //Below is the unimportant boilerplate to make the slider visible
   //////////////////////////////////////////////////////////////////
   
   public SliderExample(){
       window = new JFrame("Slider Example");
         window.setBackground(Color.BLACK);
         setBackground(Color.BLACK);
         init();
   }
   
   public void showWindow(){
       Thread looper = new Thread(this);
         looper.start();
       window.add(this, BorderLayout.CENTER);
       window.addMouseListener(this);
       window.addMouseMotionListener(this);
        window.validate();
        window.setVisible(true);
        window.pack();
   }
   
   @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        createGraphics2D((Graphics2D)g, getSize().width, getSize().height);
        //Draws a non-flickering image
        g.drawImage(bimg, 0, 0, this);
    }
   
   private void createGraphics2D(Graphics2D g2, int w, int h) {
        if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h)
            bimg = (BufferedImage) createImage(w, h);

        g2 = bimg.createGraphics();
        g2.setColor(Color.WHITE);
        g2.fillRect(0, 0, w, h);
       
        updateRender(g2);    
    }
   
   @Override
    public Dimension getPreferredSize(){
        return new Dimension(400, 300);
    }
   
   @Override
   public final void run() {
        try{          
            while(true){
                Thread.sleep(10);
                repaint();
            }
        }catch(Exception e){
            System.err.println(e.getMessage());
            System.exit(0);
        }
    }

   private void handleMouse(MouseEvent e){
      requestFocus();
        switch(e.getID()){
           case MouseEvent.MOUSE_PRESSED:  
              if(e.getButton() == 1)
                 leftClick = true;
              else if(e.getButton() == 3)
                 rightClick = true;
              break;
           case MouseEvent.MOUSE_RELEASED:
              if(e.getButton() == 1)
                 leftClick = false;
              else if(e.getButton() == 3)
                 rightClick = false;
              break;
           case MouseEvent.MOUSE_MOVED:
           case MouseEvent.MOUSE_DRAGGED:
              mouseX = e.getX();
              mouseY = e.getY();
              break;
        }
   }

   @Override
   public void mouseDragged(MouseEvent e) {
      handleMouse(e);
   }

   @Override
   public void mouseMoved(MouseEvent e) {
      handleMouse(e);
   }

   @Override
   public void mouseClicked(MouseEvent e) {
      handleMouse(e);
   }

   @Override
   public void mouseEntered(MouseEvent e) {
      handleMouse(e);
   }

   @Override
   public void mouseExited(MouseEvent e) {
      handleMouse(e);
   }

   @Override
   public void mousePressed(MouseEvent e) {
      handleMouse(e);
   }

   @Override
   public void mouseReleased(MouseEvent e) {
      handleMouse(e);
   }  
}


Hopefully, you'll be able to read through the comments to figure out how it works. If you have any questions, please ask.

Offline Dicee

Junior Devvie


Medals: 2



« Reply #13 - Posted 2013-12-11 22:13:48 »

Thanks for your help ctomni231. But the thing is I need to adapt the percentage on the width, so width scale should not change if I change the max value, I mean, it should not increase the scale width.
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #14 - Posted 2013-12-11 22:21:03 »

I had to do a little tweaking with your posted code to get it into a test harness, but I managed to confirm a suspicion. It's floating point errors that are throwing the percentages off. Give the below version a try and see if you have better luck. It just adds some rounding when doing the calculations. Even though Slick accepts them, you should really use ints when possible for doing screen positioning. Let me know if this works any better. If not, I can upload the test harness version to see if that sheds any more light on the situation. Cool

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
public class PSlider extends PAbstractComponent {

    private float value;
    private float percentage;
    private float minValue, maxValue;
   
    private float lineWidth, lineHeight, lineX, lineY;
    private float sliderBoxWidth, sliderBoxHeight, sliderBoxX, sliderBoxY;
   
    private boolean wasSliderBoxDown = false;
           
    private float minusBoxWidth, minusBoxHeight, minusBoxX, minusBoxY;
    private float plusBoxWidth, plusBoxHeight, plusBoxX, plusBoxY;
   
    private PLabel label;
   
    public PSlider(float width, float height) throws SlickException {
        super();
       
        this.width = width;
        this.height = height;
       
        this.label = new PLabel(PFontType.LucidiaFaxRegular, 26, false, false);
        this.label.setNormalColor(new Color(0, 0, 0, .75f));
       
        this.value = 0;
    }
   
    public PSlider(float width, float height, float currentValue) throws SlickException {
        this(width, height);
       
        this.value = currentValue;
    }

    @Override
    public void render(Graphics g, float x, float y) throws SlickException {
        this.x = x;
        this.y = y;
       
        // render rect and fill
        float cornerRadius = 0;
        g.setColor(new Color(0, 0, 0, .5f));
        g.fillRoundRect(x, y, width, height, (int) cornerRadius);
        g.setColor(Color.decode("#ffffbe"));
        g.drawRoundRect(x, y, width, height, (int) cornerRadius);
       
        // render line
        float lineX1 = this.lineX;
        float lineX2 = this.lineX + this.lineWidth;
        float lineY1 = this.lineY;
        float lineY2 = lineY1;
        g.setColor(Color.decode("#ffffbe"));
        g.drawLine(lineX1, lineY1, lineX2, lineY2);
       
        // render slider box
        float sBoxX = this.sliderBoxX;
        float sBoxY = this.sliderBoxY;
        float sBoxWidth = this.sliderBoxWidth;
        float sBoxHeight = this.sliderBoxHeight;
        g.setColor(new Color(223, 182, 105));
        g.fillRoundRect(sBoxX, sBoxY, sBoxWidth, sBoxHeight, (int) cornerRadius);
        g.setColor(Color.decode("#ffffbe"));
        g.drawRoundRect(sBoxX, sBoxY, sBoxWidth, sBoxHeight, (int) cornerRadius);
    }
   
    public void performAction(GameContainer gc, int mx, int my) throws SlickException {
        Input input = gc.getInput();

        this.sliderBoxWidth = Math.round(this.height / 2);
        this.sliderBoxHeight = Math.round(this.height / 2);
        this.sliderBoxY = Math.round(this.y + ((this.y + this.height) - this.y) / 2
                                 - (this.sliderBoxHeight / 2));
       
        this.lineWidth = Math.round(this.width - this.sliderBoxWidth);
        this.lineHeight = 1;
        this.lineX = Math.round(this.x + (this.sliderBoxWidth / 2));
        this.lineY = Math.round(this.y + ((this.y + this.height) - this.y) / 2);
       
        float mxRelative = mx - this.lineX;

        if (this.sliderBoxX < this.lineX - Math.round(this.sliderBoxWidth/2)) {
            this.sliderBoxX = this.lineX - Math.round(this.sliderBoxWidth/2);
        }
        else if(this.sliderBoxX > this.lineX + this.lineWidth + Math.round(this.sliderBoxWidth/2)) {
            this.sliderBoxX = this.lineX + this.lineWidth + Math.round(this.sliderBoxWidth/2);
        }
       
        // if is pressed somewhere on the line inside the sliderBox
        if (my >= this.y && my <= this.y + this.height) {
            if (input.isMousePressed(Input.MOUSE_LEFT_BUTTON)) {
                if ((mxRelative >= 0) && (mxRelative <= this.lineWidth)) {
                    this.percentage = (float) (mxRelative / this.lineWidth);
                    this.value = Math.round(this.percentage * 100);
                    this.sliderBoxX = mxRelative + this.lineX - Math.round(sliderBoxWidth / 2);
                }
            }
        }
       
        float minSliderBoxX = this.sliderBoxX;
        float minSliderBoxY = this.sliderBoxY;
        float maxSliderBoxX = this.sliderBoxX + this.sliderBoxWidth;
        float maxSliderBoxY = this.sliderBoxY + this.sliderBoxHeight;
        if ((mx > minSliderBoxX && mx < maxSliderBoxX) && (my > minSliderBoxY && my < maxSliderBoxY)) {
            if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
                wasSliderBoxDown = true;
            }
        }

       
        if(wasSliderBoxDown) {
            if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
                if (mxRelative >= 0 && mxRelative <= this.lineWidth) {
                    this.percentage = (float) (mxRelative / this.lineWidth);
                    this.value = Math.round(this.percentage * 100);
                    this.sliderBoxX = mxRelative + this.lineX - Math.round(sliderBoxWidth / 2);
                }
            } else {
                wasSliderBoxDown = false;
            }
        }
    }
   
    public void setInitialValues(float minValue, float maxValue) {
        if (this.minValue != minValue) {
            this.minValue = minValue;
        }
        if (this.maxValue != maxValue) {
            this.maxValue = maxValue;
        }
       
    }
   
    public float getValue() {
        return value;
    }

    public float getPercentage() {
        return percentage;
    }
}

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #15 - Posted 2013-12-11 22:27:15 »

holy s***, it seem it worked.. wait.. give me 5m to play with it and test it   Shocked Smiley
Offline Dicee

Junior Devvie


Medals: 2



« Reply #16 - Posted 2013-12-11 22:35:33 »

It is really working man!

"It's floating point errors that are throwing the percentages off."

Well, you know what, I would never ever think of this as a problem, even tho this problem was really pain, at least I learned some new things. Wish you a long life!
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #17 - Posted 2013-12-11 22:42:50 »

Solving problems is the funnest thing about coding. Wink Glad you're back on track. A long and happy life to you as well. Cool

@ctomni - I always see you go above and beyond to help other users. Excellent alternate example of how to implement a slider. Grin

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #18 - Posted 2013-12-12 16:54:01 »

Again me.
I totally forgot to tell you, about the initializing sliderBoxX before even all actions are performed on the slider.
So, I have this condition, because if I don't want slider box x coordinate to be initialized with 0 value:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
        // set sliderBoX X coordinate start position
        if (this.sliderBoxX < this.lineX - Math.round(this.sliderBoxWidth / 2)) {
            this.sliderBoxX = this.lineX - Math.round(this.sliderBoxWidth / 2);
           
            // set value & percentage on the min
            if (this.value != this.minValue) {
                this.value = this.minValue;
//                this.percentage = (this.value / this.maxValue) * 100.0f; // not needed
            }
        }
        else if (this.sliderBoxX > this.lineX + this.lineWidth + Math.round(this.sliderBoxWidth / 2)) {
            this.sliderBoxX = this.lineX + this.lineWidth + Math.round(this.sliderBoxWidth / 2);
           
            // set value & percentage on the max
        }


I also have the method called setInitialValues (which initializes min & max value), this method is called inside of a update method before actionPerformed method, which is needed, because, I will almost always change min & max values during the game. However, we forgot to set properly values, I mean, slider works fine if we have for a minimum value 0, but if we have any other value greater than 0 set for the minimum value, slider will still perform actions between 0 and any other maxmimum value instead of that minimum value we have set (example 200-700). I've set the minValue on the value, but that does not affect on the calculations, since that value is not affected on the any of the code, so it makes no sense. :/

Offline ctomni231

JGO Wizard


Medals: 99
Projects: 1
Exp: 7 years


Not a glitch. Just have a lil' pixelexia...


« Reply #19 - Posted 2013-12-12 17:07:09 »

@ctomni - I always see you go above and beyond to help other users. Excellent alternate example of how to implement a slider. Grin

Good help is hard to find these days. However, I don't think you are completely out of the water yet in terms of helping out. For me, I am always trying to look for as much solutions to a problem as I can. Sometimes, you never know which form would come in handy...  Pointing

Thanks for the +1 and for helping out Smiley.

Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #20 - Posted 2013-12-12 18:02:31 »

If I understand the issue you're having correctly, then try changing this line (line 114 from my previous code post):
1  
this.value = Math.round(this.percentage * 100);

To:
1  
this.value = this.minValue + Math.round((this.maxValue - this.MinValue)*percentage);

The will ensure that the values returned are between the min and max values that you set.

If I misunderstood the problem, let me know and we can have another crack at it. Smiley

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #21 - Posted 2013-12-12 18:32:37 »

There it is, that's the right way to compute, you made it Smiley. It was so simple, that I forgot the basic math...

I just made a little change, in order to get the correct percentage between 0.0-100.0%, not 0.0-1.0.

1  
2  
this.percentage = (float) (mxRelative / this.lineWidth) * 100;
this.value = this.minValue + Math.round((this.maxValue - this.minValue) * percentage) / 100;


So that I don't have to multiply in the getPercentage method, I think that's better. Anyway, thanks!
1  
2  
3  
    public float getPercentage() {
        return Float.parseFloat(decimalFormat.format(percentage));
    }


p.s. This will be a nice resource for those who want to make a nice basic slider component.
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #22 - Posted 2013-12-12 18:59:30 »

Phew, glad I understood correctly. LOL I was about to break the test version from yesterday to see what I'd missed if I didn't. I'm just happy that you're back on track again.

p.s. This will be a nice resource for those who want to make a nice basic slider component.

Indeed. Between the back and forth of working out what was going on, and the code provided by you and ctomni, there are a lot of good ideas. I think that the interesting question rightfully earns you your first medal. Don't go spending it all in one place now. Wink

Have a great one. Grin

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #23 - Posted 2013-12-17 01:17:07 »

Hi again, I was adding a new feature, so it has some connections with slider.
I assume that you have tried already TextField component that comes together with Slick2D. However, I want to share value between TextField & Slider, which means that for a value which is shown in TextField, also and Slider should have, or vice verse.

So far I have only one problem with a slider, to compute tempSliderBoxX variable based on a newValue.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
        if (this.newValue != this.value) {
            float tempSliderBoxX = 0;

            if (tempSliderBoxX >= 0 && tempSliderBoxX <= this.lineWidth) {
                if (this.newValue > this.maxValue) {
                    this.newValue = this.maxValue;
                    this.value = this.newValue;
                    this.percentage = 100.0f;
                    this.sliderBoxX = this.lineX + this.lineWidth + Math.round(this.sliderBoxWidth / 2);
                }
                else if (this.newValue < this.minValue) {
                    this.newValue = this.minValue;
                    this.value = this.newValue;
                    this.percentage = 0.0f;
                    this.sliderBoxX = this.lineX - Math.round(this.sliderBoxWidth / 2);
                }
                else {
                    this.value = this.newValue;
                    this.percentage = (float) (tempSliderBoxX / this.lineWidth) * 100;
                    this.sliderBoxX = tempSliderBoxX;
                }
            }
        }


And a second problem is about, to re-write (inside a textfield) the value after calling the method setValue().
Right now, I can get a value from the slider and use setText method on the textfield component, in order to show current slider value, and it well performs actions.
But the problem is, when we set that value to text field from a slider, it blocks me to re-write again some value in the textfield.
I can only move the cursor position with left & right arrows or backspace or delete button, but not and to remove characters from it and then write.
So I'm not sure if I'm missing some additional conditions for this or the problem is order of performing the actions.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
    @Override
    public void update(GameContainer gc, StateBasedGame sg, int delta) throws SlickException {
        Input input = gc.getInput();
        int mx = input.getMouseX();
        int my = input.getMouseY();

        setupTable.setTableStake((String) tableStakesCB.getSelectedItem());
        setupTable.setGameSpeed((String) gameSpeedCB.getSelectedItem());
        setupTable.setTableTheme((String) tableThemeCB.getSelectedItem());
       
        if (!this.hasFocus) return;

        float minValue = 500;
        float maxValue = 1000;
        // sets min & max values to slider component
        tempSlider.setInitialValues(minValue, maxValue);
        // performs actions
        tempSlider.performAction(gc, mx, my);

        // sets a slider value on the text field
        tempField.setText(String.valueOf(tempSlider.getValue()));
        System.out.println("Text field: " + tempField.getText() + " \t Slider: " + tempSlider.getValue());
       
        tableStakesCB.performAction(gc, mx, my);
        gameSpeedCB.performAction(gc, mx, my);
        tableThemeCB.performAction(gc, mx, my);
    }

        @Override
    public void componentActivated(AbstractComponent source) {
        if (!this.hasFocus) return;
       
        if (source == nameField) {
            setupTable.setPlayerName(nameField.getText());
        }
        if (source == totalPlayersField) {
            setupTable.setTotalPlayers(totalPlayersField.getText());
        }
        if (source == tempField) {
            if (tempField.getText() != null && tempField.getText().matches("[0-9]+")) {
                tempSlider.setValue(Float.parseFloat(tempField.getText()));
            }
        }
    }



I apologize for bad english, so please tell me if something was not clear.
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #24 - Posted 2013-12-17 02:25:58 »

From the looks of what you posted, I can see a couple of potential problems. "newValue" shouldn't be a member of your Slider class. It should only exist within the scope of a setter function. You're also making things a bit more complex than they need to be. The following function should do what you need. I don't have the test harness around at the moment, so I haven't had a chance to test it, but it should work (faulty hardware aside).Wink

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public void setValue(float newValue) {
   if(this.value != newValue) {
      float multiplier;
      this.value = Math.min(Math.max(this.minValue, newValue), this.maxValue);
      multiplier = (this.value/this.maxValue);
      this.percentage = multiplier * 100.0f;
      this.sliderBoxX = Math.round(this.lineX + (this.lineWidth * multiplier) - (this.sliderBoxWidth/2));
      // sets a slider value on the text field
      tempField.setText(String.valueOf(getValue()));
   }
}


Then just replace any calls to "this.value = ..." with a call to "this.setValue(...)". and move your "tempField.setText(String.valueOf(tempSlider.getValue()));" call out of the update method. Your values will be guaranteed to always be between minValue and maxValue, your slider box position will only be calculated when needed, and your text box value will only be changed when the value of the slider changes.

Now you'll need to add a key listener to your text box that will cast the String value of the text box to a float value and call your PSlider's "setValue(...)" function when the enter key is pressed. This keeps the slider value from changing before the user is finished typing in a value. If you want to get fancy, filter the key input, and only append the key values to the text box's value if the key typed is between 0 and 9. Doing it this way has the added benefit of setting the value of the text box to a valid value if the entered value is outside of the sliders minValue/maxValue range (remember that "setValue(...) takes care of this for us).

Theoretically this should work, but as the old saying goes: "In theory, there is no difference between theory and practice; in practice, there is".Smiley Let me know if you have any questions or if this isn't making sense and I'll try to get another test harness built so I can quit theorizing and start practicing.Wink

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #25 - Posted 2013-12-17 19:19:25 »

Definitely agree with your idea after I read your code, that a newValue should not be part of the class PSlider.
But the 9 line in your code, which I don't understand, how I'm going to use the instance tempField,
as it's not the member of the PSlider class?
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #26 - Posted 2013-12-17 20:38:56 »

Hmmm, I must have misread your code. I was assuming the second code snippet you posted was part of the same class as the first code snippet. Can you post a bit of information on what the class that the second snippet of code is from does/is? I'm thinking it's probably a game manager class?

Off the top of my head, one way to do this would be to create a custom "slider listener" class which could be used to perform the updates outside of the actual controls.

First define a new interface in your program:
1  
2  
3  
4  
// Define a new interface...
interface PSliderListener {
   public void valueUpdated(float newValue);
}

Now add a way for a PSlider to keep track of any interested listeners:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
public class PSlider extends PAbstractComponent {
   // Keep track of all subscribed listeners in an array list.
   private ArrayList<PSliderListener> Listeners = new ArrayList<PSliderListener>(0);
   
   // Snip...

   // Function to add listeners...
   public void addPSliderListener(PSliderListener psl) {
      // Don't add the same listener multiple times.
      if(!Listeners.contains(psl)) {
         Listeners.add(psl);
      }
   }

   // Function to remove listeners...
   public void removePSliderListener(PSliderListener psl) {
      // Don't remove non-existant listeners.
      if(Listeners.contains(psl)) {
         Listeners.remove(psl);
      }
   }

   // Tweak the existing setValue function to notify the listeners when the value is updated.
   public void setValue(float newValue) {
      if(this.value != newValue) {
         float multiplier;
         this.value = Math.min(Math.max(this.minValue, newValue), this.maxValue);
         multiplier = (this.value/this.maxValue);
         this.percentage = multiplier * 100.0f;
         this.sliderBoxX = Math.round(this.lineX + (this.lineWidth * multiplier) - (this.sliderBoxWidth/2));
         // Notify all listeners that the value of the PSlider has changed.
         for(PSliderListener psl : Listeners) {
            psl.valueUpdated(this.Value);
         }
      }
   }
}

Now you'll have to define a PSliderListener somewhere in your code that shares scope with the TextBox component (I'll just stick with the name "tempField" since I'm not sure what your actual control name is):
1  
2  
3  
4  
5  
6  
7  
8  
TextField tempField = ...;

PSliderListener valueListener = new PSliderListener() {
   @Override
   public void valueUpdated(float newValue) {
      tempField.setText(String.valueOf(newValue()));
   }
};

And finally add your listener to the PSlider (I'll refer to the slider as "tempSlider" since, again, I'm not sure of the actual control name):
1  
tempSlider.addPSliderListener(valueListener);


Doing it this way removes the "hard link" between controls and adds flexibility. You could for instance update multiple controls by sticking the needed code all within the same listener, or you could add a different listener for each control to be updated. A similar approach could be taken with the TextField as well. One thing you want to try and avoid is hard coded dependencies between your components.

Again, the above is off the top of my head and makes a lot of assumptions about how your application is structured at one level above the controls so let me know if something isn't clear or if there is a reason you can think of which might prevent you from taking that approach. If so, we can look at the problem from a different angle.Smiley

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #27 - Posted 2013-12-17 23:57:51 »

Sorry for late response, I was doing some other work.
I was afraid that you misunderstood me, my english..
But anyway, this is how the things are.
 
The first snippet, is written in actionPerform method at the end, and that method is inside PSlider class.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
    public void performAction(GameContainer gc, int mx, int my) throws SlickException {
        Input input = gc.getInput();

        this.sliderBoxWidth = Math.round(this.height / 2);
        this.sliderBoxHeight = Math.round(this.height / 2);
        this.sliderBoxY = Math.round(this.y + ((this.y + this.height) - this.y) / 2
                                 - (this.sliderBoxHeight / 2));
       
        this.lineWidth = Math.round(this.width - this.sliderBoxWidth);
        this.lineHeight = 1;
        this.lineX = Math.round(this.x + (this.sliderBoxWidth / 2));
        this.lineY = Math.round(this.y + ((this.y + this.height) - this.y) / 2);
       
        if (this.sliderBoxX < this.lineX - Math.round(this.sliderBoxWidth / 2)) {
            this.sliderBoxX = this.lineX - Math.round(this.sliderBoxWidth / 2);
           
            if (this.value != this.minValue) {
                this.value = this.minValue;
                this.newValue = this.value;
            }
        }
        else if (this.sliderBoxX > this.lineX + this.lineWidth + Math.round(this.sliderBoxWidth / 2)) {
            this.sliderBoxX = this.lineX + this.lineWidth + Math.round(this.sliderBoxWidth / 2);
           
            if (this.value != this.maxValue) {
                this.value = this.maxValue;
                this.newValue = this.value;
            }
        }
       
        float mxRelative = mx - this.lineX;
       
        if (my >= this.y && my <= this.y + this.height) {
            if (input.isMousePressed(Input.MOUSE_LEFT_BUTTON)) {
                if ((mxRelative >= 0) && (mxRelative <= this.lineWidth)) {
                    this.percentage = (float) (mxRelative / this.lineWidth) * 100;
                    this.value = this.minValue + ((this.maxValue - this.minValue) * percentage) / 100;
                    this.sliderBoxX = mxRelative + this.lineX - Math.round(sliderBoxWidth / 2);
                   
                    this.newValue = this.value;
                }
            }
        }
       
        float minSliderBoxX = this.sliderBoxX;
        float minSliderBoxY = this.sliderBoxY;
        float maxSliderBoxX = this.sliderBoxX + this.sliderBoxWidth;
        float maxSliderBoxY = this.sliderBoxY + this.sliderBoxHeight;
        if ((mx > minSliderBoxX && mx < maxSliderBoxX) && (my > minSliderBoxY && my < maxSliderBoxY)) {
            if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
                wasSliderBoxDown = true;
            }
        }

        if (wasSliderBoxDown) {
            if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
                if (mxRelative >= 0 && mxRelative <= this.lineWidth) {
                    this.percentage = (float) (mxRelative / this.lineWidth) * 100;
                    this.value = this.minValue + ((this.maxValue - this.minValue) * percentage) / 100;
                    this.sliderBoxX = mxRelative + this.lineX - Math.round(sliderBoxWidth / 2);
                   
                    this.newValue = this.value;
                }
            } else {
                wasSliderBoxDown = false;
            }
        }

       
        if (this.newValue != this.value) {
            float tempSliderBoxX = 0;

            if (tempSliderBoxX >= 0 && tempSliderBoxX <= this.lineWidth) {
                if (this.newValue > this.maxValue) {
                    this.newValue = this.maxValue;
                    this.value = this.newValue;
                    this.percentage = 100.0f;
                    this.sliderBoxX = this.lineX + this.lineWidth + Math.round(this.sliderBoxWidth / 2);
                }
                else if (this.newValue < this.minValue) {
                    this.newValue = this.minValue;
                    this.value = this.newValue;
                    this.percentage = 0.0f;
                    this.sliderBoxX = this.lineX - Math.round(this.sliderBoxWidth / 2);
                }
                else {
                    this.value = this.newValue;
                    this.percentage = (float) (tempSliderBoxX / this.lineWidth) * 100;
                    this.sliderBoxX = tempSliderBoxX;
                }
            }
        }
    }


Second snippet, the method update, is part of the state class which extends BasicGameState. The class contains update, render, init methods...
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
    @Override
    public void update(GameContainer gc, StateBasedGame sg, int delta) throws SlickException {
        Input input = gc.getInput();
        int mx = input.getMouseX();
        int my = input.getMouseY();

        setupTable.setTableStake((String) tableStakesCB.getSelectedItem());
        setupTable.setGameSpeed((String) gameSpeedCB.getSelectedItem());
        setupTable.setTableTheme((String) tableThemeCB.getSelectedItem());
       
        if (!this.hasFocus) return;

        float minValue = 500;
        float maxValue = 1000;
        // sets min & max values to slider component
        tempSlider.setInitialValues(minValue, maxValue);
        // performs actions
        tempSlider.performAction(gc, mx, my);

        // sets a slider value on the text field, if enter is pressed
        tempField.setText(String.valueOf(tempSlider.getValue()));
        System.out.println("Text field: " + tempField.getText() + " \t Slider: " + tempSlider.getValue());
       
        tableStakesCB.performAction(gc, mx, my);
        gameSpeedCB.performAction(gc, mx, my);
        tableThemeCB.performAction(gc, mx, my);
    }


One of the problems, which I was worried, when I was about to start to create these components, was listener.
I am familiar with it, and I had the opportunity to use an anonymous class listener, back when I made my first Swing project, but the problem is that I hadn't the opportunity to create the functionality to interface on the simpler examples, that will produce the listener system for the component.
So my idea was that (and since this is my first slick2d project), to make the simple components with out listener interfaces, which will use the basic functions update / render, literally acting as an entity, and I succeeded in that.
The only component that I have not still made, is a TextField, but I hope to the already existing one from Slick2D, that I will be able to do something with it.
So I can finally go to the most fun part, which is to create a game logic, which I have already had made, but as a console game.
Offline CodeHead

JGO Knight


Medals: 41


From rags to riches...to rags.


« Reply #28 - Posted 2013-12-18 02:13:50 »

Your English is better than a lot of native English speakers I know.Wink

I'll be honest; trying to get this to work via game states is a little bit of a challenge, but after your explanation of how the previous code fit into the grand scheme of things, I think I may have a serviceable solution.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
public class YourStateClass extends BasicGameState {    

   // Snip ...

   @Override
   public void update(GameContainer gc, StateBasedGame sg, int delta) throws SlickException {
      Input input = gc.getInput();
      int mx = input.getMouseX();
      int my = input.getMouseY();
   
      setupTable.setTableStake((String) tableStakesCB.getSelectedItem());
      setupTable.setGameSpeed((String) gameSpeedCB.getSelectedItem());
      setupTable.setTableTheme((String) tableThemeCB.getSelectedItem());
         
      if (!this.hasFocus) return;
   
      float minValue = 500;
      float maxValue = 1000;
      // sets min & max values to slider component
      tempSlider.setInitialValues(minValue, maxValue);
      // performs actions
      tempSlider.performAction(gc, mx, my);
   
      // sets a slider value on the text field, if the TextField doesn't have the focus.
      if(tempField.hasFocus()) {
         try {
            tempSlider.setValue(Float.valueOf(tempField.getText().trim()));
         }
         catch(NumberFormatException(nfe) {
            tempSlider.setValue(0.0f);
         }
      }
      else {
         tempField.setValue(String.valueOf(tempSlider.getValue()));
      }

      tableStakesCB.performAction(gc, mx, my);
      gameSpeedCB.performAction(gc, mx, my);
      tableThemeCB.performAction(gc, mx, my);
   }
}

The above change to your code basically checks to see if the TextField currently has the focus. If it does, then the PSlider gets it's value updated to whatever is in the text field. I've wrapped the call to setValue(...) in a try catch block to account for any possible NumberFormatExceptions that would be thrown if an un-parsable value was entered in the TextField, but I'd still advise you to filter the input. The catch block will simply cause the PSlider to be set to its minValue. If the TextField doesn't have the focus, its value will be set to the PSlider's current value. The only possible snag I can think of would be if Slick doesn't automatically set the focus to the TextField when the mouse is clicked on it, but I'm assuming that it should...I'm just hoping that if the TextField gets focus it doesn't suddenly cause the "if (!this.hasFocus) return;" check to fail. You will have to let me know if this works or not. I haven't worked with Slick's controls so I'm not completely sure of how they function within a BasicGameState object.

The only component that I have not still made, is a TextField, but I hope to the already existing one from Slick2D, that I will be able to do something with it.

I hope it works for your needs. I'm in the process of rolling my own TextBox/TextArea controls, and they can get pretty complex depending on the functionality you want to implement. They're taking a lot longer than any other control I've implemented, and I still have to figure out how to handle cursor positioning with mouse input efficiently along with debugging the scroll bar implementation that it uses.Shocked I'm afraid that figuratively, I have many "miles to go before I sleep".Cool

Here's hoping that the above gets you closer to "the most fun part".Grin

Edit: I put together a quick StateBasedGame so I could experiment with the BasicGameState and the TextField. After looking over your code again, I realized that "this.hasFocus" must refer to a variable that you've defined within your BasicGameState which means that you don't have to worry about invalidating the check when the TextField gets focus. On the "bad news" side, it doesn't appear that there is a direct way to filter the input to the TextField, but since the try/catch block handles any invalid input, you should be fine.Cheesy

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Dicee

Junior Devvie


Medals: 2



« Reply #29 - Posted 2013-12-18 17:54:50 »

Quote
I'm just hoping that if the TextField gets focus it doesn't suddenly cause the "if (!this.hasFocus) return;" check to fail.

Don't worry. Smiley
I should rename hasFocus into something else.
Anyway, the idea was to block the other components in updating, if any window, for example dialog, pops up.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
public abstract class PAbstractState extends BasicGameState {

    protected boolean hasFocus;
   
    protected PAbstractState() {
        this.hasFocus = true;
    }

    public boolean hasFocus() {
        return hasFocus;
    }

    public void enableFocus() {
        this.hasFocus = true;
    }
   
    public void disableFocus() {
        this.hasFocus = false;
    }
   
}


So, function works like pause/unpause. I just set "this" which points to PAbstractState, as a argument to the component method,
for components like a Dialog, in my case for now it's only PDialog.
So it's something like this,

1  
String s = JOptionPane.showConfirmDialog(this, "", "", );


But instead of static calling, I'm using the instance of the PDialog..
And simply, when some action occurs, dialog pops up, the method is activated and changes stance of the hasFocus variable for this state...

Anyway, let's back on the topic.
It seems that it is working. I tested quickly, unfortunately I do not have much time to play with programming now,
but the reason why I'm saying "seems",
for a min/max value 0/1000 SliderBox X coordinate, moves ok,
but for 500/1000, SliderBox for a min value starts from the center of the slider line, instead at the minX or start X of the slider line.
What is really interesting with this, no matter how it starts from the center of the line,
it still moves (computing)properly for that half line width, until 1000 or a maxX of the line. Cheesy

However,  I will leave for tomorrow, this testing, don't hate me Cheesy, because I have to go tonight to watch a Hobit movie Smiley,
but tommorow I will do test, and try to figure out.
So I will inform you tomorrow, what I assume that cause this.
 
Pages: [1] 2
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

rwatson462 (38 views)
2014-12-15 09:26:44

Mr.CodeIt (31 views)
2014-12-14 19:50:38

BurntPizza (62 views)
2014-12-09 22:41:13

BurntPizza (99 views)
2014-12-08 04:46:31

JscottyBieshaar (60 views)
2014-12-05 12:39:02

SHC (74 views)
2014-12-03 16:27:13

CopyableCougar4 (77 views)
2014-11-29 21:32:03

toopeicgaming1999 (138 views)
2014-11-26 15:22:04

toopeicgaming1999 (127 views)
2014-11-26 15:20:36

toopeicgaming1999 (38 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!