Completed
Pull Request — master (#270)
by
unknown
03:29
created

LayoutScrollable::addChildren()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace eXpansion\Framework\Gui\Layouts;
4
5
use eXpansion\Framework\Gui\Components\AbstractUiElement;
6
use eXpansion\Framework\Gui\Components\Scrollbar;
7
use FML\Controls\Frame;
8
use FML\Controls\Quad;
9
use FML\Elements\Format;
10
use FML\Script\Features\ScriptFeature;
11
use FML\Script\Script;
12
use FML\Script\ScriptLabel;
13
use FML\Types\Container;
14
use FML\Types\Renderable;
15
use FML\Types\ScriptFeatureable;
16
17
class LayoutScrollable extends AbstractUiElement implements Renderable, ScriptFeatureable, Container
18
{
19
20
    protected $force = false;
21
    protected $_X = 0;
22
    protected $_Y = 0;
23
    protected $offset = 0;
24
    protected $scrollbarH = true;
25
    protected $scrollbarV = true;
26
    protected $parentFrame = null;
27
    protected $frame_posX = 0;
28
    protected $frame_posY = 0;
29
30
    /**
31
     * layoutScrollable constructor.
32
     * @param Container $frame
33
     * @param           $sizeX
34
     * @param           $sizeY
35
     */
36
    public function __construct(Container $frame, $sizeX, $sizeY)
37
    {
38
        $this->parentFrame = $frame;
39
        $this->frame_posX = $frame->getX();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface FML\Types\Container as the method getX() does only exist in the following implementations of said interface: FML\Controls\Frame, FML\Controls\Frame3d, eXpansion\Framework\Gui\Components\Button, eXpansion\Framework\Gui\Components\ConfirmButton, eXpansion\Framework\Gui\Layouts\LayoutLine, eXpansion\Framework\Gui\Layouts\LayoutRow, eXpansion\Framework\Gui\Layouts\LayoutScrollable.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
40
        $this->frame_posY = $frame->getY();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface FML\Types\Container as the method getY() does only exist in the following implementations of said interface: FML\Controls\Frame, FML\Controls\Frame3d, eXpansion\Framework\Gui\Components\Button, eXpansion\Framework\Gui\Components\ConfirmButton, eXpansion\Framework\Gui\Layouts\LayoutLine, eXpansion\Framework\Gui\Layouts\LayoutRow, eXpansion\Framework\Gui\Layouts\LayoutScrollable.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
41
        $this->setSize($sizeX, $sizeY);
42
43
        $frame->setPosition(0, 0);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface FML\Types\Container as the method setPosition() does only exist in the following implementations of said interface: FML\Controls\Frame, FML\Controls\Frame3d, eXpansion\Framework\Gui\Components\Button, eXpansion\Framework\Gui\Components\ConfirmButton, eXpansion\Framework\Gui\Layouts\LayoutLine, eXpansion\Framework\Gui\Layouts\LayoutRow, eXpansion\Framework\Gui\Layouts\LayoutScrollable.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
44
    }
45
46
    /**
47
     * Set the axis which supports scrolling
48
     * default all axis enabled
49
     * @param bool $x
50
     * @param bool $y
51
     *
52
     * @return $this
53
     */
54
    public function setAxis($x, $y)
55
    {
56
        $this->scrollbarH = $x;
57
        $this->scrollbarV = $y;
58
59
        return $this;
60
    }
61
62
    /**
63
     * Forces container size.
64
     * @param $x
65
     * @param $y
66
     */
67
    public function forceContainerSize($x, $y)
68
    {
69
        $this->force = true;
70
        $this->_X = $x;
71
        $this->_Y = $y;
72
    }
73
74
    /**
75
     * Render the XML element
76
     *
77
     * @param \DOMDocument $domDocument DOMDocument for which the XML element should be rendered
78
     * @return \DOMElement
79
     */
80
    public function render(\DOMDocument $domDocument)
81
    {
82
        $container = new Frame();
83
        $container->setPosition($this->frame_posX, $this->frame_posY);
84
85
        $quad = new Quad();
86
        $quad->setStyles('Bgs1', 'BgColorContour')
87
            ->setSize($this->getWidth(), $this->getHeight());
88
89
        $contentFrame = new Frame();
90
        $contentFrame->addChild($this->parentFrame);
0 ignored issues
show
Documentation introduced by
$this->parentFrame is of type object<FML\Types\Container>, but the function expects a object<FML\Types\Renderable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
91
92
        $container->addChild($quad);
93
        $container->addChild($contentFrame);
94 View Code Duplication
        if ($this->scrollbarV) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
95
            $contentFrame->setSize($this->width - 5, $this->height);
96
            $this->offset = 5;
97
            $container->addChild(new Scrollbar(
98
                "Y",
99
                $this->getWidth(),
100
                0,
101
                10,
102
                $this->getHeight()
103
            ));
104
        }
105
106 View Code Duplication
        if ($this->scrollbarH) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
            $contentFrame->setSize($this->width - 5, $this->height - 5);
108
            $container->addChild(new Scrollbar(
109
                "X",
110
                0,
111
                -$this->getHeight(),
112
                10,
113
                $this->getWidth() - $this->offset
114
            ));
115
        }
116
117
118
        return $container->render($domDocument);
119
    }
120
121
    /**
122
     * Get the Script Features
123
     *
124
     * @return ScriptFeature[]
125
     */
126
    public function getScriptFeatures()
127
    {
128
        $features = [];
129
        if ($this->parentFrame instanceof ScriptFeatureable) {
130
            $features[] = $this->parentFrame->getScriptFeatures();
131
        }
132
        $features[] = $this;
133
134
        return ScriptFeature::collect($features);
135
    }
136
137
138
    /**
139
     * Prepare the given Script for rendering by adding the needed Labels, etc.
140
     *
141
     * @param Script $script Script to prepare
142
     * @return void
143
     */
144
    public function prepare(Script $script)
145
    {
146
        $script->addCustomScriptLabel(ScriptLabel::MouseClick, $this->getScriptMouseClick());
147
        $script->addCustomScriptLabel(ScriptLabel::OnInit, $this->getScriptInit());
148
        $script->addCustomScriptLabel(ScriptLabel::Loop, $this->getScriptLoop());
149
    }
150
151
    protected function getScriptInit()
152
    {
153
        $offset = number_format($this->offset, 1, ".", "");
154
155
        return /** @lang textmate */
156
            <<<EOL
157
            
158
            declare CMlFrame exp_scroll_frame = Null;
159
            declare CMlFrame exp_scroll_content = Null;
160
            declare Vec2 exp_scroll_content_size = <0.,0.>; 
161
            declare Boolean exp_scroll_activeY = False;
162
            declare Boolean exp_scroll_activeX = False;
163
            declare Vec2 exp_scroll_pos = <0.,0.>;
164
            declare Real exp_scroll_offset = $offset;
165
EOL;
166
    }
167
168
169
    protected function getScriptMouseClick()
170
    {
171
        return /** @lang textmate */
172
            <<<EOL
173
            
174
            if (Event.Control != Null && Event.Control.HasClass("uiScrollbar") )  {
175
                if (Event.Control.DataAttributeGet("axis") == "X") {
176
                    exp_scroll_activeX = True;
177
                    log(Now ^ "X");
178
                } else {
179
                    exp_scroll_activeY = True;																
180
                }
181
                exp_scroll_frame = Event.Control.Parent;
182
                exp_scroll_pos = <MouseX, MouseY> - Event.Control.RelativePosition_V3 ;
183
                exp_scroll_content = (exp_scroll_frame.Parent.Controls[1] as CMlFrame); // gets the bounding frame				
184
                exp_scroll_content_size = exp_scroll_content.Controls[0].Size;
185
                log(exp_scroll_content_size);
186
            }
187
EOL;
188
    }
189
190
191
    protected function getScriptLoop()
192
    {
193
        return /** @lang textmate */
194
            <<<EOL
195
196
        if (exp_scroll_activeY) {		
197
					declare Real pos = (MouseY - exp_scroll_pos.Y) ;
198
					declare Real upperLimit = exp_scroll_frame.RelativePosition_V3.Y - exp_scroll_frame.RelativePosition_V3.Y - 5.;
199
					declare Real lowerLimit =  upperLimit - exp_scroll_frame.Controls[3].Size.Y + exp_scroll_frame.Controls[0].Size.Y + 10.;
200
					
201
					if (pos > upperLimit) {
202
						pos = upperLimit;
203
					}
204
															
205
					if (pos < lowerLimit)  {  
206
						pos = lowerLimit;
207
					}
208
					
209
				declare Real start = (upperLimit - pos);
210
				declare Real diff = MathLib::Abs(lowerLimit - upperLimit);
211
								
212
				exp_scroll_frame.Controls[0].RelativePosition_V3.Y = pos; // update scrollbar position												
213
				exp_scroll_content.Controls[0].RelativePosition_V3.Y = (start / diff) * (exp_scroll_content_size.Y - exp_scroll_frame.Parent.Controls[0].Size.Y + 10.);  //  gets the content frame
214
		}
215
		
216
		if (exp_scroll_activeX) {		
217
					declare Real pos = ( MouseX - exp_scroll_pos.X);
218
					declare Real leftLimit = 5.;
219
					declare Real rightLimit =  leftLimit + exp_scroll_frame.Controls[3].Size.X - exp_scroll_frame.Controls[0].Size.X -10.;
220
					
221
					if (pos < leftLimit) {
222
						pos = leftLimit;
223
					}
224
															
225
					if (pos > rightLimit)  {  
226
						pos = rightLimit;
227
					}
228
					
229
				declare Real start =  (leftLimit - pos);
230
				declare Real diff = MathLib::Abs(leftLimit + rightLimit);
231
								
232
				exp_scroll_frame.Controls[0].RelativePosition_V3.X = pos; // update scrollbar position												
233
				exp_scroll_content.Controls[0].RelativePosition_V3.X = (start / diff) * (exp_scroll_content_size.X + 10);  //  gets the content frame
234
		}
235
		
236
		
237
		
238
		if (MouseLeftButton == False)  {
239
			exp_scroll_activeX = False;
240
			exp_scroll_activeY = False;
241
		}
242
243
EOL;
244
    }
245
246
    /**
247
     * Get the children
248
     *
249
     * @api
250
     * @return Renderable[]
251
     */
252
    public function getChildren()
253
    {
254
        return $this->parentFrame->getChildren();
255
    }
256
257
    /**
258
     * Add a new child
259
     *
260
     * @api
261
     * @param Renderable $child Child Control to add
262
     * @return static
263
     */
264
    public function addChild(Renderable $child)
265
    {
266
        $this->parentFrame->addChild($child);
267
268
        return $this;
269
    }
270
271
    /**
272
     * Add a new child
273
     *
274
     * @api
275
     * @param Renderable $child Child Control to add
276
     * @return static
277
     * @deprecated Use addChild()
278
     * @see        Container::addChild()
279
     */
280
    public function add(Renderable $child)
281
    {
282
        // do nothing
283
    }
284
285
    /**
286
     * Add new children
287
     *
288
     * @api
289
     * @param Renderable[] $children Child Controls to add
290
     * @return static
291
     */
292
    public function addChildren(array $children)
293
    {
294
        $this->parentFrame->addChildren($children);
295
296
        return $this;
297
    }
298
299
    /**
300
     * Remove all children
301
     *
302
     * @api
303
     * @return static
304
     */
305
    public function removeAllChildren()
306
    {
307
        $this->parentFrame->removeAllChildren();
308
309
        return $this;
310
    }
311
312
    /**
313
     * Remove all children
314
     *
315
     * @api
316
     * @return static
317
     * @deprecated Use removeAllChildren()
318
     * @see        Container::removeAllChildren()
319
     */
320
    public function removeChildren()
321
    {
322
        // do nothing
323
    }
324
325
    /**
326
     * Get the Format
327
     *
328
     * @api
329
     * @param bool $createIfEmpty If the format should be created if it doesn't exist yet
330
     * @return Format
331
     * @deprecated Use Style
332
     * @see        Style
333
     */
334
    public function getFormat($createIfEmpty = true)
335
    {
336
        // do nothing
337
    }
338
339
    /**
340
     * Set the Format
341
     *
342
     * @api
343
     * @param Format $format New Format
344
     * @return static
345
     * @deprecated Use Style
346
     * @see        Style
347
     */
348
    public function setFormat(Format $format = null)
349
    {
350
        // do nothing
351
    }
352
}
353