Skip to content

Commit

Permalink
Merge pull request #21 from skoro/widget/progressbar
Browse files Browse the repository at this point in the history
Implementation of progressbar widget
  • Loading branch information
skoro authored Nov 6, 2022
2 parents ede241a + 10f7b1e commit de2b5b3
Show file tree
Hide file tree
Showing 5 changed files with 371 additions and 0 deletions.
197 changes: 197 additions & 0 deletions demos/progressbar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
<?php

declare(strict_types=1);

use Tkui\Layouts\Pack;
use Tkui\TclTk\Variable;
use Tkui\Widgets\Buttons\Button;
use Tkui\Widgets\Container;
use Tkui\Widgets\Frame;
use Tkui\Widgets\Label;
use Tkui\Widgets\LabelFrame;
use Tkui\Widgets\Progressbar;

require_once __DIR__ . '/DemoAppWindow.php';

$demo = new class extends DemoAppWindow
{
const MAX_VALUE = 100;

private Variable $progressValue;
/** @var array<Progressbar> */
private array $autoincrementBars;

public function __construct()
{
parent::__construct('Progressbar Demo');

$this->progressValue = $this->app->registerVar('progressValue');
$this->progressValue->set(0);

$this->buildActionButtons($this);

$this->autoincrementBars = array_merge(
$this->buildHorizontalProgressBars($this),
$this->buildVerticalProgressbars($this)
);
}

/**
* @return array<Progressbar>
*/
private function buildHorizontalProgressBars(Container $parent): array
{
$f = new LabelFrame($parent, 'Horizontal');

$pbar1 = new Progressbar($f, [
'mode' => Progressbar::MODE_DETERMINATE,
'orient' => Progressbar::ORIENT_HORIZONTAL,
'variable' => $this->progressValue,
'maximum' => self::MAX_VALUE,
]);

$pbar2 = new Progressbar($f, [
'mode' => Progressbar::MODE_INDETERMINATE,
'orient' => Progressbar::ORIENT_HORIZONTAL,
]);

$f->grid(new Label($f, 'Determinate'), [
'row' => 0,
'column' => 0,
'sticky' => 'w'
]);
$f->grid($pbar1, [
'pady' => 4,
'row' => 0,
'column' => 1,
]);
$f->grid(new Label($f, 'Value:'), ['row' => 1, 'column' => 0, 'sticky' => 'w']);
$f->grid(new Label($f, '-', ['textVariable' => $this->progressValue]), [
'row' => 1,
'column' => 1,
'sticky' => 'w',
]);

$f->grid(new Label($f, 'Indeterminate'), [
'row' => 2,
'column' => 0,
'sticky' => 'w',
]);
$f->grid($pbar2, [
'pady' => 4,
'row' => 2,
'column' => 1,
]);

$parent->pack($f, [
'side' => Pack::SIDE_LEFT,
'padx' => 4,
'pady' => 6,
'anchor' => 'n',
]);

return [$pbar1, $pbar2];
}

private function buildVerticalProgressbars(Container $parent): array
{
$f = new LabelFrame($parent, 'Vertical');

$pbar1 = new Progressbar($f, [
'mode' => Progressbar::MODE_DETERMINATE,
'orient' => Progressbar::ORIENT_VERTICAL,
'variable' => $this->progressValue,
'maximum' => self::MAX_VALUE,
]);

$pbar2 = new Progressbar($f, [
'mode' => Progressbar::MODE_INDETERMINATE,
'orient' => Progressbar::ORIENT_VERTICAL,
]);

$f->grid(new Label($f, 'Determinate'), [
'row' => 0,
'column' => 0,
'sticky' => 'w'
]);
$f->grid($pbar1, [
'pady' => 4,
'row' => 0,
'column' => 1,
]);
$f->grid(new Label($f, '-', ['textVariable' => $this->progressValue]), [
'row' => 1,
'column' => 1,
]);

$f->grid(new Label($f, 'Indeterminate'), [
'row' => 0,
'column' => 3,
'sticky' => 'w',
]);
$f->grid($pbar2, [
'pady' => 4,
'row' => 0,
'column' => 4,
]);

$parent->pack($f, [
'side' => Pack::SIDE_LEFT,
'padx' => 4,
'pady' => 6,
'anchor' => 'n',
]);

return [$pbar1, $pbar2];
}

private function buildActionButtons(Container $parent): void
{
$f = new Frame($parent);

$btnStep = new Button($f, 'Step');
$btnStep->onClick([$this, 'stepProgress']);

$btnStart = new Button($f, 'Start');
$btnStart->onClick([$this, 'startProgress']);

$btnStop = new Button($f, 'Stop');
$btnStop->onClick([$this, 'stopProgress']);

$f->pack([$btnStep, $btnStart, $btnStop], [
'side' => Pack::SIDE_LEFT,
]);

$parent->pack($f, [
'side' => Pack::SIDE_BOTTOM,
'pady' => 8,
'padx' => 4,
]);
}

public function startProgress(): void
{
$this->progressValue->set(0);
foreach ($this->autoincrementBars as $progressBar) {
$progressBar->start();
}
}

public function stopProgress(): void
{
foreach ($this->autoincrementBars as $progressBar) {
$progressBar->stop();
}
}

public function stepProgress(): void
{
$value = $this->progressValue->asFloat() + 5.0;
if ($value >= self::MAX_VALUE) {
$value = 0;
}
$this->progressValue->set($value);
}
};

$demo->run();
14 changes: 14 additions & 0 deletions src/Widgets/Consts/ProgressMode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Tkui\Widgets\Consts;

/**
* Values for 'mode' property of progressbar widget.
*/
interface ProgressMode
{
const MODE_DETERMINATE = 'determinate';
const MODE_INDETERMINATE = 'indeterminate';
}
85 changes: 85 additions & 0 deletions src/Widgets/Progressbar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);

namespace Tkui\Widgets;

use Tkui\Options;
use Tkui\TclTk\Variable;
use Tkui\Widgets\Common\ValueInVariable;
use Tkui\Widgets\Consts\Orient;
use Tkui\Widgets\Consts\ProgressMode;

/**
* Implementation of Ttk progressbar widget.
*
* @link https://www.tcl.tk/man/tcl8.6/TkCmd/ttk_progressbar.html
*
* @property int $length
* @property float $maximum
* @property string $mode
* @property string $orient
* @property float $value
* @property Variable $variable
*/
class Progressbar extends TtkWidget implements ValueInVariable, Orient, ProgressMode
{
protected string $widget = 'ttk::progressbar';
protected string $name = 'prbr';

/**
* @inheritdoc
*/
protected function initWidgetOptions(): Options
{
return new Options([
'length' => null,
'maximum' => null,
'mode' => null,
'orient' => Orient::ORIENT_HORIZONTAL,
'phase' => null,
'value' => null,
'variable' => null,
]);
}

public function getValue()
{
return $this->value;
}

public function setValue($value): self
{
$this->value = $value;
return $this;
}

/**
* Begins autoincrement mode.
*
* @param int $interval Interval milliseconds.
*/
public function start(int $interval = 50): self
{
$this->call('start', $interval);
return $this;
}

/**
* Stops autoincrement mode.
*/
public function stop(): self
{
$this->call('stop');
return $this;
}

/**
* Increments a progressbar value by amount.
*/
public function step(float $amount = 1.0): self
{
$this->call('step', $amount);
return $this;
}
}
2 changes: 2 additions & 0 deletions src/Widgets/TkWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ protected function call(string $method, ...$args): string
*/
public function __get(string $name)
{
// TODO: this won't work when the widget changes the option
// internally by itself, like progressbar.
$value = $this->options->$name;
if ($value === null) {
$value = $this->call('cget', Options::getTclOption($name));
Expand Down
73 changes: 73 additions & 0 deletions tests/Widgets/ProgressbarTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace Tkui\Tests\Widgets;

use Tkui\Tests\TestCase;
use Tkui\Widgets\Progressbar;

class ProgressbarTest extends TestCase
{
/** @test */
public function it_issues_progressbar_create_command(): void
{
$this->tclEvalTest(1, [
['ttk::progressbar', $this->checkWidget('.prbr')],
]);

new Progressbar($this->createWindowStub());
}

/** @test */
public function it_issues_progressbar_with_options(): void
{
$this->tclEvalTest(1, [
[
'ttk::progressbar', $this->checkWidget('.prbr'),
'-maximum', '100',
'-mode', 'determinate',
'-orient', 'vertical',
],
]);

new Progressbar($this->createWindowStub(), [
'mode' => Progressbar::MODE_DETERMINATE,
'maximum' => 100,
'orient' => Progressbar::ORIENT_VERTICAL,
]);
}

/** @test */
public function it_issues_progressbar_start_command(): void
{
$this->tclEvalTest(2, [
['ttk::progressbar', $this->checkWidget('.prbr')],
[$this->checkWidget('.prbr'), 'start', '150'],
]);

(new Progressbar($this->createWindowStub()))->start(150);
}

/** @test */
public function it_issues_progressbar_stop_command(): void
{
$this->tclEvalTest(2, [
['ttk::progressbar', $this->checkWidget('.prbr')],
[$this->checkWidget('.prbr'), 'stop'],
]);

(new Progressbar($this->createWindowStub()))->stop();
}

/** @test */
public function it_issues_progressbar_step_command(): void
{
$this->tclEvalTest(2, [
['ttk::progressbar', $this->checkWidget('.prbr')],
[$this->checkWidget('.prbr'), 'step', '1.5'],
]);

(new Progressbar($this->createWindowStub()))->step(1.5);
}
}

0 comments on commit de2b5b3

Please sign in to comment.