Skip to content

Commit de2b5b3

Browse files
authored
Merge pull request #21 from skoro/widget/progressbar
Implementation of progressbar widget
2 parents ede241a + 10f7b1e commit de2b5b3

File tree

5 files changed

+371
-0
lines changed

5 files changed

+371
-0
lines changed

demos/progressbar.php

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Tkui\Layouts\Pack;
6+
use Tkui\TclTk\Variable;
7+
use Tkui\Widgets\Buttons\Button;
8+
use Tkui\Widgets\Container;
9+
use Tkui\Widgets\Frame;
10+
use Tkui\Widgets\Label;
11+
use Tkui\Widgets\LabelFrame;
12+
use Tkui\Widgets\Progressbar;
13+
14+
require_once __DIR__ . '/DemoAppWindow.php';
15+
16+
$demo = new class extends DemoAppWindow
17+
{
18+
const MAX_VALUE = 100;
19+
20+
private Variable $progressValue;
21+
/** @var array<Progressbar> */
22+
private array $autoincrementBars;
23+
24+
public function __construct()
25+
{
26+
parent::__construct('Progressbar Demo');
27+
28+
$this->progressValue = $this->app->registerVar('progressValue');
29+
$this->progressValue->set(0);
30+
31+
$this->buildActionButtons($this);
32+
33+
$this->autoincrementBars = array_merge(
34+
$this->buildHorizontalProgressBars($this),
35+
$this->buildVerticalProgressbars($this)
36+
);
37+
}
38+
39+
/**
40+
* @return array<Progressbar>
41+
*/
42+
private function buildHorizontalProgressBars(Container $parent): array
43+
{
44+
$f = new LabelFrame($parent, 'Horizontal');
45+
46+
$pbar1 = new Progressbar($f, [
47+
'mode' => Progressbar::MODE_DETERMINATE,
48+
'orient' => Progressbar::ORIENT_HORIZONTAL,
49+
'variable' => $this->progressValue,
50+
'maximum' => self::MAX_VALUE,
51+
]);
52+
53+
$pbar2 = new Progressbar($f, [
54+
'mode' => Progressbar::MODE_INDETERMINATE,
55+
'orient' => Progressbar::ORIENT_HORIZONTAL,
56+
]);
57+
58+
$f->grid(new Label($f, 'Determinate'), [
59+
'row' => 0,
60+
'column' => 0,
61+
'sticky' => 'w'
62+
]);
63+
$f->grid($pbar1, [
64+
'pady' => 4,
65+
'row' => 0,
66+
'column' => 1,
67+
]);
68+
$f->grid(new Label($f, 'Value:'), ['row' => 1, 'column' => 0, 'sticky' => 'w']);
69+
$f->grid(new Label($f, '-', ['textVariable' => $this->progressValue]), [
70+
'row' => 1,
71+
'column' => 1,
72+
'sticky' => 'w',
73+
]);
74+
75+
$f->grid(new Label($f, 'Indeterminate'), [
76+
'row' => 2,
77+
'column' => 0,
78+
'sticky' => 'w',
79+
]);
80+
$f->grid($pbar2, [
81+
'pady' => 4,
82+
'row' => 2,
83+
'column' => 1,
84+
]);
85+
86+
$parent->pack($f, [
87+
'side' => Pack::SIDE_LEFT,
88+
'padx' => 4,
89+
'pady' => 6,
90+
'anchor' => 'n',
91+
]);
92+
93+
return [$pbar1, $pbar2];
94+
}
95+
96+
private function buildVerticalProgressbars(Container $parent): array
97+
{
98+
$f = new LabelFrame($parent, 'Vertical');
99+
100+
$pbar1 = new Progressbar($f, [
101+
'mode' => Progressbar::MODE_DETERMINATE,
102+
'orient' => Progressbar::ORIENT_VERTICAL,
103+
'variable' => $this->progressValue,
104+
'maximum' => self::MAX_VALUE,
105+
]);
106+
107+
$pbar2 = new Progressbar($f, [
108+
'mode' => Progressbar::MODE_INDETERMINATE,
109+
'orient' => Progressbar::ORIENT_VERTICAL,
110+
]);
111+
112+
$f->grid(new Label($f, 'Determinate'), [
113+
'row' => 0,
114+
'column' => 0,
115+
'sticky' => 'w'
116+
]);
117+
$f->grid($pbar1, [
118+
'pady' => 4,
119+
'row' => 0,
120+
'column' => 1,
121+
]);
122+
$f->grid(new Label($f, '-', ['textVariable' => $this->progressValue]), [
123+
'row' => 1,
124+
'column' => 1,
125+
]);
126+
127+
$f->grid(new Label($f, 'Indeterminate'), [
128+
'row' => 0,
129+
'column' => 3,
130+
'sticky' => 'w',
131+
]);
132+
$f->grid($pbar2, [
133+
'pady' => 4,
134+
'row' => 0,
135+
'column' => 4,
136+
]);
137+
138+
$parent->pack($f, [
139+
'side' => Pack::SIDE_LEFT,
140+
'padx' => 4,
141+
'pady' => 6,
142+
'anchor' => 'n',
143+
]);
144+
145+
return [$pbar1, $pbar2];
146+
}
147+
148+
private function buildActionButtons(Container $parent): void
149+
{
150+
$f = new Frame($parent);
151+
152+
$btnStep = new Button($f, 'Step');
153+
$btnStep->onClick([$this, 'stepProgress']);
154+
155+
$btnStart = new Button($f, 'Start');
156+
$btnStart->onClick([$this, 'startProgress']);
157+
158+
$btnStop = new Button($f, 'Stop');
159+
$btnStop->onClick([$this, 'stopProgress']);
160+
161+
$f->pack([$btnStep, $btnStart, $btnStop], [
162+
'side' => Pack::SIDE_LEFT,
163+
]);
164+
165+
$parent->pack($f, [
166+
'side' => Pack::SIDE_BOTTOM,
167+
'pady' => 8,
168+
'padx' => 4,
169+
]);
170+
}
171+
172+
public function startProgress(): void
173+
{
174+
$this->progressValue->set(0);
175+
foreach ($this->autoincrementBars as $progressBar) {
176+
$progressBar->start();
177+
}
178+
}
179+
180+
public function stopProgress(): void
181+
{
182+
foreach ($this->autoincrementBars as $progressBar) {
183+
$progressBar->stop();
184+
}
185+
}
186+
187+
public function stepProgress(): void
188+
{
189+
$value = $this->progressValue->asFloat() + 5.0;
190+
if ($value >= self::MAX_VALUE) {
191+
$value = 0;
192+
}
193+
$this->progressValue->set($value);
194+
}
195+
};
196+
197+
$demo->run();
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tkui\Widgets\Consts;
6+
7+
/**
8+
* Values for 'mode' property of progressbar widget.
9+
*/
10+
interface ProgressMode
11+
{
12+
const MODE_DETERMINATE = 'determinate';
13+
const MODE_INDETERMINATE = 'indeterminate';
14+
}

src/Widgets/Progressbar.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tkui\Widgets;
6+
7+
use Tkui\Options;
8+
use Tkui\TclTk\Variable;
9+
use Tkui\Widgets\Common\ValueInVariable;
10+
use Tkui\Widgets\Consts\Orient;
11+
use Tkui\Widgets\Consts\ProgressMode;
12+
13+
/**
14+
* Implementation of Ttk progressbar widget.
15+
*
16+
* @link https://www.tcl.tk/man/tcl8.6/TkCmd/ttk_progressbar.html
17+
*
18+
* @property int $length
19+
* @property float $maximum
20+
* @property string $mode
21+
* @property string $orient
22+
* @property float $value
23+
* @property Variable $variable
24+
*/
25+
class Progressbar extends TtkWidget implements ValueInVariable, Orient, ProgressMode
26+
{
27+
protected string $widget = 'ttk::progressbar';
28+
protected string $name = 'prbr';
29+
30+
/**
31+
* @inheritdoc
32+
*/
33+
protected function initWidgetOptions(): Options
34+
{
35+
return new Options([
36+
'length' => null,
37+
'maximum' => null,
38+
'mode' => null,
39+
'orient' => Orient::ORIENT_HORIZONTAL,
40+
'phase' => null,
41+
'value' => null,
42+
'variable' => null,
43+
]);
44+
}
45+
46+
public function getValue()
47+
{
48+
return $this->value;
49+
}
50+
51+
public function setValue($value): self
52+
{
53+
$this->value = $value;
54+
return $this;
55+
}
56+
57+
/**
58+
* Begins autoincrement mode.
59+
*
60+
* @param int $interval Interval milliseconds.
61+
*/
62+
public function start(int $interval = 50): self
63+
{
64+
$this->call('start', $interval);
65+
return $this;
66+
}
67+
68+
/**
69+
* Stops autoincrement mode.
70+
*/
71+
public function stop(): self
72+
{
73+
$this->call('stop');
74+
return $this;
75+
}
76+
77+
/**
78+
* Increments a progressbar value by amount.
79+
*/
80+
public function step(float $amount = 1.0): self
81+
{
82+
$this->call('step', $amount);
83+
return $this;
84+
}
85+
}

src/Widgets/TkWidget.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ protected function call(string $method, ...$args): string
130130
*/
131131
public function __get(string $name)
132132
{
133+
// TODO: this won't work when the widget changes the option
134+
// internally by itself, like progressbar.
133135
$value = $this->options->$name;
134136
if ($value === null) {
135137
$value = $this->call('cget', Options::getTclOption($name));

tests/Widgets/ProgressbarTest.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tkui\Tests\Widgets;
6+
7+
use Tkui\Tests\TestCase;
8+
use Tkui\Widgets\Progressbar;
9+
10+
class ProgressbarTest extends TestCase
11+
{
12+
/** @test */
13+
public function it_issues_progressbar_create_command(): void
14+
{
15+
$this->tclEvalTest(1, [
16+
['ttk::progressbar', $this->checkWidget('.prbr')],
17+
]);
18+
19+
new Progressbar($this->createWindowStub());
20+
}
21+
22+
/** @test */
23+
public function it_issues_progressbar_with_options(): void
24+
{
25+
$this->tclEvalTest(1, [
26+
[
27+
'ttk::progressbar', $this->checkWidget('.prbr'),
28+
'-maximum', '100',
29+
'-mode', 'determinate',
30+
'-orient', 'vertical',
31+
],
32+
]);
33+
34+
new Progressbar($this->createWindowStub(), [
35+
'mode' => Progressbar::MODE_DETERMINATE,
36+
'maximum' => 100,
37+
'orient' => Progressbar::ORIENT_VERTICAL,
38+
]);
39+
}
40+
41+
/** @test */
42+
public function it_issues_progressbar_start_command(): void
43+
{
44+
$this->tclEvalTest(2, [
45+
['ttk::progressbar', $this->checkWidget('.prbr')],
46+
[$this->checkWidget('.prbr'), 'start', '150'],
47+
]);
48+
49+
(new Progressbar($this->createWindowStub()))->start(150);
50+
}
51+
52+
/** @test */
53+
public function it_issues_progressbar_stop_command(): void
54+
{
55+
$this->tclEvalTest(2, [
56+
['ttk::progressbar', $this->checkWidget('.prbr')],
57+
[$this->checkWidget('.prbr'), 'stop'],
58+
]);
59+
60+
(new Progressbar($this->createWindowStub()))->stop();
61+
}
62+
63+
/** @test */
64+
public function it_issues_progressbar_step_command(): void
65+
{
66+
$this->tclEvalTest(2, [
67+
['ttk::progressbar', $this->checkWidget('.prbr')],
68+
[$this->checkWidget('.prbr'), 'step', '1.5'],
69+
]);
70+
71+
(new Progressbar($this->createWindowStub()))->step(1.5);
72+
}
73+
}

0 commit comments

Comments
 (0)