mirror of https://github.com/YGGverse/Yoda.git
yggverse
4 months ago
47 changed files with 1927 additions and 2069 deletions
@ -1,28 +1,24 @@ |
|||||||
# Yoda is [PHP-GTK](https://github.com/scorninpc/php-gtk3) Browser for [Gemini Protocol](https://geminiprotocol.net) |
# Yoda is [PHP-GTK](https://github.com/scorninpc/php-gtk3) Browser for [Gemini Protocol](https://geminiprotocol.net) |
||||||
|
|
||||||
At this moment project under development! |
At this moment project in development! |
||||||
|
|
||||||
## Protocols |
## Install |
||||||
|
|
||||||
* [x] Gemini |
1. Build latest [PHP-GTK3](https://github.com/scorninpc/php-gtk3) from sources or get [Appimage](https://github.com/scorninpc/php-gtk3/releases) |
||||||
* [x] Nex |
2. `apt install git composer` |
||||||
|
3. `git clone https://github.com/YGGverse/Yoda.git` |
||||||
|
4. `cd Yoda` |
||||||
|
5. `composer update` |
||||||
|
|
||||||
## Features |
## Launch |
||||||
|
|
||||||
* [x] Custom DNS resolver with memory cache (useful for alt networks like [Yggdrasil](https://github.com/yggdrasil-network/yggdrasil-go)) |
``` bash |
||||||
* [x] Flexible settings in `config.json`, then UI |
/path/to/php-gtk3 src/Yoda.php |
||||||
* [x] Native GTK environment, no custom colors until you change it by `css` |
``` |
||||||
* [x] Multi-tabs |
|
||||||
* [x] Navigation history |
|
||||||
* [ ] Bookmarks |
|
||||||
* [ ] Certificate features |
|
||||||
* [ ] Local snaps to make resources accessible even offline |
|
||||||
* [ ] `Gemfeed` reader |
|
||||||
* [ ] Search engine integrations, probably [Yo!](https://github.com/YGGverse/Yo/tree/gemini) Search by default |
|
||||||
* [ ] Machine translations (e.g. [Lingva API](https://github.com/thedaviddelta/lingva-translate)) |
|
||||||
|
|
||||||
## Components |
## Components |
||||||
|
|
||||||
* [gemini-php](https://github.com/YGGverse/gemini-php) - PHP 8 library for Gemini protocol |
* [gemini-php](https://github.com/YGGverse/gemini-php) - Gemini protocol connections |
||||||
* [gemtext-php](https://github.com/YGGverse/gemtext-php) - PHP 8 library for Gemtext operations |
* [gemtext-php](https://github.com/YGGverse/gemtext-php) - Gemtext operations |
||||||
* [net-php](https://github.com/YGGverse/net-php) - PHP 8 library for DNS resolver and address parser |
* [net-php](https://github.com/YGGverse/net-php) - DNS resolver and network address features |
||||||
|
* [nex-php](https://github.com/YGGverse/nex-php) - NEX protocol connections |
@ -1,227 +0,0 @@ |
|||||||
{ |
|
||||||
"name":"Yoda", |
|
||||||
"theme":"Default", |
|
||||||
"database": |
|
||||||
{ |
|
||||||
"name":"database.sqlite", |
|
||||||
"username":null, |
|
||||||
"password":null |
|
||||||
}, |
|
||||||
"header": |
|
||||||
{ |
|
||||||
"enabled":true, |
|
||||||
"button": |
|
||||||
{ |
|
||||||
"close":true |
|
||||||
} |
|
||||||
}, |
|
||||||
"width":640, |
|
||||||
"height":480, |
|
||||||
"tab": |
|
||||||
{ |
|
||||||
"page": |
|
||||||
{ |
|
||||||
"title": |
|
||||||
{ |
|
||||||
"default":"New page", |
|
||||||
"width": |
|
||||||
{ |
|
||||||
"chars":32 |
|
||||||
}, |
|
||||||
"ellipsize": |
|
||||||
{ |
|
||||||
"mode":3 |
|
||||||
}, |
|
||||||
"postfix": |
|
||||||
{ |
|
||||||
"hostname":true |
|
||||||
} |
|
||||||
}, |
|
||||||
"redirect": |
|
||||||
{ |
|
||||||
"follow": |
|
||||||
{ |
|
||||||
"enabled":true, |
|
||||||
"max":5, |
|
||||||
"code": |
|
||||||
[ |
|
||||||
30, |
|
||||||
31 |
|
||||||
] |
|
||||||
} |
|
||||||
}, |
|
||||||
"resolver": |
|
||||||
{ |
|
||||||
"enabled":true, |
|
||||||
"request": |
|
||||||
{ |
|
||||||
"timeout":1, |
|
||||||
"host": |
|
||||||
[ |
|
||||||
"1.1.1.1", |
|
||||||
"8.8.8.8" |
|
||||||
], |
|
||||||
"record": |
|
||||||
[ |
|
||||||
"A", |
|
||||||
"AAAA" |
|
||||||
] |
|
||||||
}, |
|
||||||
"result": |
|
||||||
{ |
|
||||||
"shuffle":false, |
|
||||||
"cache": |
|
||||||
{ |
|
||||||
"timeout":3600 |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
"history": |
|
||||||
{ |
|
||||||
"memory": |
|
||||||
{ |
|
||||||
"enabled":true |
|
||||||
}, |
|
||||||
"database": |
|
||||||
{ |
|
||||||
"enabled":true, |
|
||||||
"mode": |
|
||||||
{ |
|
||||||
"renew":true |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
"progressbar": |
|
||||||
{ |
|
||||||
"visible":true |
|
||||||
}, |
|
||||||
"header": |
|
||||||
{ |
|
||||||
"margin":8, |
|
||||||
"button": |
|
||||||
{ |
|
||||||
"home": |
|
||||||
{ |
|
||||||
"visible":true, |
|
||||||
"label":"Home", |
|
||||||
"url":"yoda://welcome" |
|
||||||
}, |
|
||||||
"back": |
|
||||||
{ |
|
||||||
"visible":true, |
|
||||||
"label":"Back" |
|
||||||
}, |
|
||||||
"forward": |
|
||||||
{ |
|
||||||
"visible":true, |
|
||||||
"label":"Forward" |
|
||||||
}, |
|
||||||
"base": |
|
||||||
{ |
|
||||||
"visible":true, |
|
||||||
"label":"Base" |
|
||||||
}, |
|
||||||
"go": |
|
||||||
{ |
|
||||||
"visible":true, |
|
||||||
"label":"Go" |
|
||||||
} |
|
||||||
}, |
|
||||||
"entry": |
|
||||||
{ |
|
||||||
"request": |
|
||||||
{ |
|
||||||
"placeholder":"URL or any search term...", |
|
||||||
"length": |
|
||||||
{ |
|
||||||
"max":1024 |
|
||||||
}, |
|
||||||
"autocomplete": |
|
||||||
{ |
|
||||||
"enabled":true, |
|
||||||
"inline": |
|
||||||
{ |
|
||||||
"completion":true, |
|
||||||
"selection":true |
|
||||||
}, |
|
||||||
"key": |
|
||||||
{ |
|
||||||
"length":1 |
|
||||||
}, |
|
||||||
"ignore": |
|
||||||
{ |
|
||||||
"keycode": |
|
||||||
[ |
|
||||||
111, |
|
||||||
116 |
|
||||||
] |
|
||||||
}, |
|
||||||
"result": |
|
||||||
{ |
|
||||||
"limit":15 |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
"body": |
|
||||||
{ |
|
||||||
"margin":8 |
|
||||||
}, |
|
||||||
"footer": |
|
||||||
{ |
|
||||||
"margin":8, |
|
||||||
"status": |
|
||||||
{ |
|
||||||
"open": |
|
||||||
{ |
|
||||||
"complete":"{REQUEST_BASE_URL} | {TIME_C} | {RESPONSE_META} | {RESPONSE_LENGTH} bytes | {RESPONSE_SECONDS} seconds" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
"history": |
|
||||||
{ |
|
||||||
"enabled":true, |
|
||||||
"label":"History", |
|
||||||
"clean": |
|
||||||
{ |
|
||||||
"timeout":null |
|
||||||
}, |
|
||||||
"time": |
|
||||||
{ |
|
||||||
"format":"c" |
|
||||||
}, |
|
||||||
"header": |
|
||||||
{ |
|
||||||
"margin":8, |
|
||||||
"button": |
|
||||||
{ |
|
||||||
"open": |
|
||||||
{ |
|
||||||
"visible":true, |
|
||||||
"label":"Open" |
|
||||||
}, |
|
||||||
"delete": |
|
||||||
{ |
|
||||||
"visible":true, |
|
||||||
"label":"Delete" |
|
||||||
}, |
|
||||||
"search": |
|
||||||
{ |
|
||||||
"visible":true, |
|
||||||
"label":"Search" |
|
||||||
} |
|
||||||
}, |
|
||||||
"filter": |
|
||||||
{ |
|
||||||
"placeholder":"Search in history..." |
|
||||||
} |
|
||||||
}, |
|
||||||
"body": |
|
||||||
{ |
|
||||||
"margin":8 |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,41 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Abstract\Entity; |
||||||
|
|
||||||
|
abstract class Button |
||||||
|
{ |
||||||
|
public \GtkButton $gtk; |
||||||
|
|
||||||
|
protected bool $_sensitive = false; |
||||||
|
protected string $_label = 'Button'; |
||||||
|
|
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->gtk = new \GtkButton; |
||||||
|
|
||||||
|
$this->gtk->set_sensitive( |
||||||
|
$this->_sensitive |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_label( |
||||||
|
$this->_label |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->connect( |
||||||
|
'clicked', |
||||||
|
function( |
||||||
|
\GtkButton $entity |
||||||
|
) { |
||||||
|
$this->_onClick( |
||||||
|
$entity |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
abstract protected function _onClick( |
||||||
|
\GtkButton $entity |
||||||
|
): void; |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Abstract\Entity; |
||||||
|
|
||||||
|
abstract class Entry |
||||||
|
{ |
||||||
|
public \GtkEntry $gtk; |
||||||
|
|
||||||
|
private int $_length = 1024; |
||||||
|
private string $_placeholder = ''; |
||||||
|
private string $_value = ''; |
||||||
|
|
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->gtk = new \GtkEntry; |
||||||
|
|
||||||
|
$this->gtk->set_placeholder_text( |
||||||
|
$this->_placeholder |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_max_length( |
||||||
|
$this->_length |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_text( |
||||||
|
$this->_value |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->connect( |
||||||
|
'activate', |
||||||
|
function( |
||||||
|
\GtkEntry $entry |
||||||
|
) { |
||||||
|
$this->_onActivate( |
||||||
|
$entry |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->connect( |
||||||
|
'key-release-event', |
||||||
|
function ( |
||||||
|
\GtkEntry $entry, |
||||||
|
\GdkEvent $event |
||||||
|
) { |
||||||
|
$this->_onKeyRelease( |
||||||
|
$entry, |
||||||
|
$event |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
abstract protected function _onActivate( |
||||||
|
\GtkEntry $entry |
||||||
|
): void; |
||||||
|
|
||||||
|
abstract protected function _onKeyRelease( |
||||||
|
\GtkEntry $entry, |
||||||
|
\GdkEvent $event |
||||||
|
): void; |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Abstract\Entity\Window\Tab\Address\Navbar; |
||||||
|
|
||||||
|
abstract class Button extends \Yggverse\Yoda\Abstract\Entity\Button |
||||||
|
{ |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar $navbar; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\Address\Navbar $navbar |
||||||
|
) { |
||||||
|
parent::__construct(); |
||||||
|
|
||||||
|
$this->navbar = $navbar; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Abstract\Entity\Window\Tab\Address\Navbar; |
||||||
|
|
||||||
|
abstract class Entry extends \Yggverse\Yoda\Abstract\Entity\Entry |
||||||
|
{ |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar $navbar; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\Address\Navbar $navbar |
||||||
|
) { |
||||||
|
parent::__construct(); |
||||||
|
|
||||||
|
$this->navbar = $navbar; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Abstract\Entity\Window\Tab\History\Navbar; |
||||||
|
|
||||||
|
abstract class Button extends \Yggverse\Yoda\Abstract\Entity\Button |
||||||
|
{ |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History\Navbar $navbar; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\History\Navbar $navbar |
||||||
|
) { |
||||||
|
parent::__construct(); |
||||||
|
|
||||||
|
$this->navbar = $navbar; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Abstract\Entity\Window\Tab\History\Navbar; |
||||||
|
|
||||||
|
abstract class Entry extends \Yggverse\Yoda\Abstract\Entity\Entry |
||||||
|
{ |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History\Navbar $navbar; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\History\Navbar $navbar |
||||||
|
) { |
||||||
|
parent::__construct(); |
||||||
|
|
||||||
|
$this->navbar = $navbar; |
||||||
|
} |
||||||
|
} |
@ -1,217 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace Yggverse\Yoda\Entity; |
|
||||||
|
|
||||||
class App |
|
||||||
{ |
|
||||||
public \Yggverse\Yoda\Model\Config $config; |
|
||||||
public \Yggverse\Yoda\Model\Database $database; |
|
||||||
|
|
||||||
public \Yggverse\Yoda\Entity\Tab\History $history; |
|
||||||
|
|
||||||
public \GtkWindow $window; |
|
||||||
public \GtkHeaderBar $header; |
|
||||||
public \GtkNotebook $tabs; |
|
||||||
|
|
||||||
public function __construct() |
|
||||||
{ |
|
||||||
// Init config |
|
||||||
$this->config = new \Yggverse\Yoda\Model\Config; |
|
||||||
|
|
||||||
// Init database |
|
||||||
$this->database = new \Yggverse\Yoda\Model\Database( |
|
||||||
$this->config->database->name, |
|
||||||
$this->config->database->username, |
|
||||||
$this->config->database->password |
|
||||||
); |
|
||||||
|
|
||||||
// Init theme |
|
||||||
$css = new \GtkCssProvider(); |
|
||||||
|
|
||||||
$css->load_from_data( |
|
||||||
\Yggverse\Yoda\Model\File::getTheme( |
|
||||||
$this->config->theme |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$style = new \GtkStyleContext(); |
|
||||||
|
|
||||||
$style->add_provider_for_screen( |
|
||||||
$css, |
|
||||||
600 |
|
||||||
); |
|
||||||
|
|
||||||
// Init window |
|
||||||
$this->window = new \GtkWindow; |
|
||||||
|
|
||||||
$this->window->set_size_request( |
|
||||||
$this->config->width, |
|
||||||
$this->config->height |
|
||||||
); |
|
||||||
|
|
||||||
if ($this->config->header->enabled) |
|
||||||
{ |
|
||||||
$this->header = new \GtkHeaderBar; |
|
||||||
|
|
||||||
$this->header->set_title( |
|
||||||
$this->config->name |
|
||||||
); |
|
||||||
|
|
||||||
$this->header->set_show_close_button( |
|
||||||
$this->config->header->button->close |
|
||||||
); |
|
||||||
|
|
||||||
$this->window->set_titlebar( |
|
||||||
$this->header |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Init tabs |
|
||||||
$this->tabs = new \GtkNotebook; |
|
||||||
|
|
||||||
$this->tabs->set_scrollable( |
|
||||||
true |
|
||||||
); |
|
||||||
|
|
||||||
// + button |
|
||||||
$this->tabs->append_page( |
|
||||||
new \GtkLabel, |
|
||||||
new \GtkLabel( |
|
||||||
'+' |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
// History features |
|
||||||
if ($this->config->tab->history->enabled) |
|
||||||
{ |
|
||||||
$this->history = new \Yggverse\Yoda\Entity\Tab\History( |
|
||||||
$this |
|
||||||
); |
|
||||||
|
|
||||||
$this->tabs->append_page( |
|
||||||
$this->history->box, |
|
||||||
new \GtkLabel( |
|
||||||
$this->config->tab->history->label |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$this->tabs->set_tab_reorderable( |
|
||||||
$this->history->box, |
|
||||||
true |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Append blank page |
|
||||||
$page = $this->blankPage(); |
|
||||||
|
|
||||||
$page->open( |
|
||||||
$this->config->tab->page->header->button->home->url // @TODO |
|
||||||
); |
|
||||||
|
|
||||||
// Render |
|
||||||
$this->window->add( |
|
||||||
$this->tabs |
|
||||||
); |
|
||||||
|
|
||||||
$this->window->show_all(); |
|
||||||
|
|
||||||
// Init event listener |
|
||||||
$this->tabs->connect( |
|
||||||
'switch-page', |
|
||||||
function ( |
|
||||||
\GtkNotebook $tabs, |
|
||||||
\GtkWidget $child, |
|
||||||
int $position |
|
||||||
) { |
|
||||||
// Update window title on tab change |
|
||||||
$this->setTitle( |
|
||||||
$tabs->get_tab_label($child)->get_text() |
|
||||||
); |
|
||||||
|
|
||||||
// Add new tab event |
|
||||||
if ('+' == $tabs->get_tab_label($child)->get_text()) |
|
||||||
{ |
|
||||||
\Gtk::timeout_add( |
|
||||||
0, |
|
||||||
function() |
|
||||||
{ |
|
||||||
$this->blankPage(); |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
); |
|
||||||
|
|
||||||
$this->window->connect( |
|
||||||
'destroy', |
|
||||||
function() |
|
||||||
{ |
|
||||||
\Gtk::main_quit(); |
|
||||||
} |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function blankPage(): \Yggverse\Yoda\Entity\Tab\Page |
|
||||||
{ |
|
||||||
$page = new \Yggverse\Yoda\Entity\Tab\Page( |
|
||||||
$this |
|
||||||
); |
|
||||||
|
|
||||||
$this->tabs->append_page( |
|
||||||
$page->box, |
|
||||||
new \GtkLabel( |
|
||||||
$this->config->tab->page->title->default |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$this->tabs->set_tab_reorderable( |
|
||||||
$page->box, |
|
||||||
true |
|
||||||
); |
|
||||||
|
|
||||||
$this->tabs->show_all(); |
|
||||||
|
|
||||||
$this->tabs->set_current_page( |
|
||||||
$this->tabs->page_num( |
|
||||||
$page->box |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
return $page; |
|
||||||
} |
|
||||||
|
|
||||||
public function setTitle( |
|
||||||
?string $value = null |
|
||||||
): void |
|
||||||
{ |
|
||||||
if ($value) |
|
||||||
{ |
|
||||||
if ($value == 'Welcome to Yoda!') |
|
||||||
{ |
|
||||||
$title = $value; |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
$title = sprintf( |
|
||||||
'%s - %s', |
|
||||||
$value, |
|
||||||
$this->config->name |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
$title = $this->config->name; |
|
||||||
} |
|
||||||
|
|
||||||
$this->header->set_title( |
|
||||||
$title |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
@ -1,397 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace Yggverse\Yoda\Entity\Tab; |
|
||||||
|
|
||||||
class History |
|
||||||
{ |
|
||||||
public \Yggverse\Yoda\Entity\App $app; |
|
||||||
|
|
||||||
public \GtkBox $box, |
|
||||||
$header, |
|
||||||
$body; |
|
||||||
|
|
||||||
public \GtkButton $open, |
|
||||||
$delete, |
|
||||||
$search; |
|
||||||
|
|
||||||
public \GtkEntry $filter; |
|
||||||
|
|
||||||
public \GtkListStore $list; |
|
||||||
public \GtkTreeView $treeview; |
|
||||||
public \GtkScrolledWindow $container; |
|
||||||
|
|
||||||
public object $config; |
|
||||||
|
|
||||||
public function __construct( |
|
||||||
\Yggverse\Yoda\Entity\App $app |
|
||||||
) { |
|
||||||
// Init app |
|
||||||
$this->app = $app; |
|
||||||
|
|
||||||
// Init config namespace |
|
||||||
$this->config = $app->config->tab->history; |
|
||||||
|
|
||||||
// Cleanup expired history |
|
||||||
if ($this->config->clean->timeout) |
|
||||||
{ |
|
||||||
$this->app->database->cleanHistory( |
|
||||||
$this->config->clean->timeout |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Compose header |
|
||||||
$this->header = new \GtkBox( |
|
||||||
\GtkOrientation::HORIZONTAL |
|
||||||
); |
|
||||||
|
|
||||||
$this->header->set_margin_top( |
|
||||||
$this->config->header->margin |
|
||||||
); |
|
||||||
|
|
||||||
$this->header->set_margin_bottom( |
|
||||||
$this->config->header->margin |
|
||||||
); |
|
||||||
|
|
||||||
$this->header->set_margin_start( |
|
||||||
$this->config->header->margin |
|
||||||
); |
|
||||||
|
|
||||||
$this->header->set_margin_end( |
|
||||||
$this->config->header->margin |
|
||||||
); |
|
||||||
|
|
||||||
$this->header->set_spacing( |
|
||||||
$this->config->header->margin |
|
||||||
); |
|
||||||
|
|
||||||
// Open button |
|
||||||
$this->open = \GtkButton::new_with_label( |
|
||||||
$this->config->header->button->open->label |
|
||||||
); |
|
||||||
|
|
||||||
$this->open->set_sensitive( |
|
||||||
false |
|
||||||
); |
|
||||||
|
|
||||||
if ($this->config->header->button->open->visible) |
|
||||||
{ |
|
||||||
$this->header->add( |
|
||||||
$this->open |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Delete button |
|
||||||
$this->delete = \GtkButton::new_with_label( |
|
||||||
$this->config->header->button->delete->label |
|
||||||
); |
|
||||||
|
|
||||||
$this->delete->set_sensitive( |
|
||||||
false |
|
||||||
); |
|
||||||
|
|
||||||
if ($this->config->header->button->delete->visible) |
|
||||||
{ |
|
||||||
$this->header->add( |
|
||||||
$this->delete |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Filter field |
|
||||||
$this->filter = new \GtkEntry; |
|
||||||
|
|
||||||
$this->filter->set_placeholder_text( |
|
||||||
$this->config->header->filter->placeholder |
|
||||||
); |
|
||||||
|
|
||||||
$this->header->pack_start( |
|
||||||
$this->filter, |
|
||||||
true, |
|
||||||
true, |
|
||||||
0 |
|
||||||
); |
|
||||||
|
|
||||||
// Search button |
|
||||||
$this->search = \GtkButton::new_with_label( |
|
||||||
$this->config->header->button->search->label |
|
||||||
); |
|
||||||
|
|
||||||
if ($this->config->header->button->search->visible) |
|
||||||
{ |
|
||||||
$this->header->add( |
|
||||||
$this->search |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Build history list |
|
||||||
$this->treeview = new \GtkTreeView(); |
|
||||||
|
|
||||||
$this->treeview->append_column( |
|
||||||
new \GtkTreeViewColumn( |
|
||||||
'Time', |
|
||||||
new \GtkCellRendererText(), |
|
||||||
'text', |
|
||||||
1 |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$this->treeview->append_column( |
|
||||||
new \GtkTreeViewColumn( |
|
||||||
'Title', |
|
||||||
new \GtkCellRendererText(), |
|
||||||
'text', |
|
||||||
2 |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$this->treeview->append_column( |
|
||||||
new \GtkTreeViewColumn( |
|
||||||
'URL', |
|
||||||
new \GtkCellRendererText(), |
|
||||||
'text', |
|
||||||
3 |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
// Init list storage |
|
||||||
$this->list = new \GtkListStore( |
|
||||||
\GObject::TYPE_INT, |
|
||||||
\GObject::TYPE_STRING, |
|
||||||
\GObject::TYPE_STRING, |
|
||||||
\GObject::TYPE_STRING |
|
||||||
); |
|
||||||
|
|
||||||
$this->treeview->set_model( |
|
||||||
$this->list |
|
||||||
); |
|
||||||
|
|
||||||
/* @TODO row-activated |
|
||||||
$this->treeview->get_selection()->set_mode( |
|
||||||
\GtkSelectionMode::MULTIPLE |
|
||||||
); |
|
||||||
*/ |
|
||||||
|
|
||||||
// Compose body |
|
||||||
$this->body = new \GtkBox( |
|
||||||
\GtkOrientation::VERTICAL |
|
||||||
); |
|
||||||
|
|
||||||
$this->container = new \GtkScrolledWindow(); |
|
||||||
|
|
||||||
$this->container->add( |
|
||||||
$this->treeview |
|
||||||
); |
|
||||||
|
|
||||||
$this->body->set_margin_start( |
|
||||||
$this->config->body->margin |
|
||||||
); |
|
||||||
|
|
||||||
$this->body->pack_start( |
|
||||||
$this->container, |
|
||||||
true, |
|
||||||
true, |
|
||||||
0 |
|
||||||
); |
|
||||||
|
|
||||||
// Compose page |
|
||||||
$this->box = new \GtkBox( |
|
||||||
\GtkOrientation::VERTICAL |
|
||||||
); |
|
||||||
|
|
||||||
$this->box->add( |
|
||||||
$this->header |
|
||||||
); |
|
||||||
|
|
||||||
$this->box->pack_start( |
|
||||||
$this->body, |
|
||||||
true, |
|
||||||
true, |
|
||||||
0 |
|
||||||
); |
|
||||||
|
|
||||||
// Refresh history |
|
||||||
$this->refresh(); |
|
||||||
|
|
||||||
// Activate events |
|
||||||
$this->treeview->connect( |
|
||||||
'row-activated', |
|
||||||
function ($tree) |
|
||||||
{ |
|
||||||
if ($url = $this->getSelectedColumn(3, $tree)) |
|
||||||
{ |
|
||||||
$page = $this->app->blankPage(); |
|
||||||
|
|
||||||
$page->open( |
|
||||||
$url |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
); |
|
||||||
|
|
||||||
$this->treeview->connect( |
|
||||||
'cursor-changed', |
|
||||||
function ($tree) |
|
||||||
{ |
|
||||||
$url = $this->getSelectedColumn( |
|
||||||
3, $tree |
|
||||||
); |
|
||||||
|
|
||||||
$this->open->set_sensitive( |
|
||||||
(bool) $url |
|
||||||
); |
|
||||||
|
|
||||||
$this->delete->set_sensitive( |
|
||||||
(bool) $url |
|
||||||
); |
|
||||||
} |
|
||||||
); |
|
||||||
|
|
||||||
$this->filter->connect( |
|
||||||
'activate', |
|
||||||
function ($entry) |
|
||||||
{ |
|
||||||
$this->refresh( |
|
||||||
$entry->get_text() |
|
||||||
); |
|
||||||
} |
|
||||||
); |
|
||||||
|
|
||||||
if ($this->config->header->button->open->visible) |
|
||||||
{ |
|
||||||
$this->open->connect( |
|
||||||
'clicked', |
|
||||||
function () |
|
||||||
{ |
|
||||||
if ($url = $this->getSelectedColumn(3)) |
|
||||||
{ |
|
||||||
$page = $this->app->blankPage(); |
|
||||||
|
|
||||||
$page->open( |
|
||||||
$url |
|
||||||
); |
|
||||||
|
|
||||||
$this->refresh(); |
|
||||||
} |
|
||||||
} |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
if ($this->config->header->button->delete->visible) |
|
||||||
{ |
|
||||||
$this->delete->connect( |
|
||||||
'clicked', |
|
||||||
function () |
|
||||||
{ |
|
||||||
if ($id = $this->getSelectedColumn(0)) |
|
||||||
{ |
|
||||||
$this->app->database->deleteHistory( |
|
||||||
$id |
|
||||||
); |
|
||||||
|
|
||||||
$this->refresh(); |
|
||||||
} |
|
||||||
} |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
if ($this->config->header->button->search->visible) |
|
||||||
{ |
|
||||||
$this->search->connect( |
|
||||||
'clicked', |
|
||||||
function () |
|
||||||
{ |
|
||||||
$this->refresh( |
|
||||||
$this->filter->get_text() |
|
||||||
); |
|
||||||
} |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function refresh(): void |
|
||||||
{ |
|
||||||
// Reset previous state |
|
||||||
$this->list->clear(); |
|
||||||
|
|
||||||
// Update buttons sensibility |
|
||||||
$this->open->set_sensitive( |
|
||||||
false |
|
||||||
); |
|
||||||
|
|
||||||
$this->delete->set_sensitive( |
|
||||||
false |
|
||||||
); |
|
||||||
|
|
||||||
// Build history list from database records |
|
||||||
foreach ($this->app->database->getHistory($this->filter->get_text()) as $record) |
|
||||||
{ |
|
||||||
$this->list->append( |
|
||||||
[ |
|
||||||
$record->id, |
|
||||||
date( |
|
||||||
$this->config->time->format, |
|
||||||
$record->time |
|
||||||
), |
|
||||||
$record->title, |
|
||||||
$record->url |
|
||||||
] |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Update tree |
|
||||||
$this->treeview->show_all(); |
|
||||||
} |
|
||||||
|
|
||||||
public function add( |
|
||||||
string $url, |
|
||||||
?string $title = null, |
|
||||||
bool $renew = false // delete previous records with same URL |
|
||||||
): ?int |
|
||||||
{ |
|
||||||
if ($renew) |
|
||||||
{ |
|
||||||
foreach ($this->app->database->getHistory($url) as $record) |
|
||||||
{ |
|
||||||
$this->app->database->deleteHistory( |
|
||||||
$record->id |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$id = $this->app->database->addHistory( |
|
||||||
$url, |
|
||||||
$title |
|
||||||
); |
|
||||||
|
|
||||||
$this->refresh(); |
|
||||||
|
|
||||||
return $id; |
|
||||||
} |
|
||||||
|
|
||||||
public function getSelectedColumn( |
|
||||||
int $column, |
|
||||||
\GtkTreeView $treeview = null |
|
||||||
): null|int|string |
|
||||||
{ |
|
||||||
if (is_null($treeview)) |
|
||||||
{ |
|
||||||
$treeview = $this->treeview; |
|
||||||
} |
|
||||||
|
|
||||||
list( |
|
||||||
$list, |
|
||||||
$row |
|
||||||
) = $treeview->get_selection()->get_selected(); |
|
||||||
|
|
||||||
if ($list && $row) |
|
||||||
{ |
|
||||||
if ($value = $list->get_value($row, $column)) |
|
||||||
{ |
|
||||||
return $value; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,53 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Header; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab; |
||||||
|
|
||||||
|
class Window |
||||||
|
{ |
||||||
|
public \GtkWindow $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Model\Database $database; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Header $header; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab $tab; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private int $_width = 640; |
||||||
|
private int $_height = 480; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Model\Database $database |
||||||
|
) { |
||||||
|
$this->database = $database; |
||||||
|
|
||||||
|
$this->gtk = new \GtkWindow; |
||||||
|
|
||||||
|
$this->gtk->set_size_request( |
||||||
|
$this->_width, |
||||||
|
$this->_height |
||||||
|
); |
||||||
|
|
||||||
|
$this->header = new Header( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_titlebar( |
||||||
|
$this->header->gtk |
||||||
|
); |
||||||
|
|
||||||
|
$this->tab = new Tab( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->tab->gtk |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->show_all(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window; |
||||||
|
|
||||||
|
class Header |
||||||
|
{ |
||||||
|
public \GtkHeaderBar $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window $window; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private bool $_actions = true; |
||||||
|
private string $_title = 'Yoda'; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window $window |
||||||
|
) { |
||||||
|
$this->window = $window; |
||||||
|
|
||||||
|
$this->gtk = new \GtkHeaderBar; |
||||||
|
|
||||||
|
$this->gtk->set_show_close_button( |
||||||
|
$this->_actions |
||||||
|
); |
||||||
|
|
||||||
|
$this->setTitle( |
||||||
|
$this->_title |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function setTitle( |
||||||
|
?string $title = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->gtk->set_title( |
||||||
|
is_null($title) ? $this->_title : sprintf( |
||||||
|
'%s - %s', |
||||||
|
$title, |
||||||
|
$this->_title |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\History; |
||||||
|
|
||||||
|
class Tab |
||||||
|
{ |
||||||
|
public \GtkNotebook $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window $window; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private bool $_reorderable = true; |
||||||
|
private bool $_scrollable = true; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window $window |
||||||
|
) { |
||||||
|
$this->window = $window; |
||||||
|
|
||||||
|
$this->gtk = new \GtkNotebook; |
||||||
|
|
||||||
|
$this->gtk->set_scrollable( |
||||||
|
$this->_scrollable |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->connect( |
||||||
|
'switch-page', |
||||||
|
function ( |
||||||
|
\GtkNotebook $entity, |
||||||
|
\GtkWidget $child, |
||||||
|
int $position |
||||||
|
) { |
||||||
|
$this->window->header->setTitle( |
||||||
|
$entity->get_tab_label( |
||||||
|
$child |
||||||
|
)->get_text() |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
$this->append( // @TODO remove |
||||||
|
new History( |
||||||
|
$this |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->append( // @TODO remove |
||||||
|
new Address( |
||||||
|
$this |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function append( |
||||||
|
Address | History $entity, |
||||||
|
?bool $reorderable = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->gtk->append_page( |
||||||
|
$entity->gtk, |
||||||
|
$entity->title->gtk |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_tab_reorderable( |
||||||
|
$entity->gtk, |
||||||
|
is_null($reorderable) ? $this->_reorderable : $reorderable |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->show_all(); |
||||||
|
|
||||||
|
// Focus on appended tab |
||||||
|
$this->gtk->set_current_page( |
||||||
|
$this->gtk->page_num( |
||||||
|
$entity->gtk |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
// Update application title |
||||||
|
$this->window->header->setTitle( |
||||||
|
$entity->title->gtk->get_text() |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,228 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Title; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Content; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Statusbar; |
||||||
|
|
||||||
|
class Address |
||||||
|
{ |
||||||
|
public \GtkBox $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab $tab; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Title $title; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar $navbar; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Content $content; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Statusbar $statusbar; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab $tab |
||||||
|
) { |
||||||
|
$this->tab = $tab; |
||||||
|
|
||||||
|
$this->title = new Title( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk = new \GtkBox( |
||||||
|
\GtkOrientation::VERTICAL |
||||||
|
); |
||||||
|
|
||||||
|
$this->navbar = new Navbar( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->navbar->gtk |
||||||
|
); |
||||||
|
|
||||||
|
$this->content = new Content( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->pack_start( |
||||||
|
$this->content->gtk, |
||||||
|
true, |
||||||
|
true, |
||||||
|
0 |
||||||
|
); |
||||||
|
|
||||||
|
$this->statusbar = new Statusbar( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->statusbar->gtk |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function update(): void |
||||||
|
{ |
||||||
|
// Parse address |
||||||
|
$address = new \Yggverse\Net\Address( |
||||||
|
$this->navbar->request->gtk->get_text() |
||||||
|
); |
||||||
|
|
||||||
|
// Update title |
||||||
|
$this->title->gtk->set_text( |
||||||
|
$address->getHost() |
||||||
|
); |
||||||
|
|
||||||
|
// Update navbar elements |
||||||
|
$this->navbar->base->update( |
||||||
|
$address |
||||||
|
); |
||||||
|
|
||||||
|
// Remember address in the navigation memory |
||||||
|
$this->navbar->history->add( |
||||||
|
$address->get() |
||||||
|
); |
||||||
|
|
||||||
|
// Refresh history in database |
||||||
|
$this->navbar->address->tab->window->database->refreshHistory( |
||||||
|
$address->get(), |
||||||
|
// @TODO title |
||||||
|
); |
||||||
|
|
||||||
|
// Update statusbar indicator |
||||||
|
$this->statusbar->gtk->set_text( |
||||||
|
'Loading...' |
||||||
|
); |
||||||
|
|
||||||
|
// Detect protocol |
||||||
|
switch ($address->getScheme()) |
||||||
|
{ |
||||||
|
case 'file': |
||||||
|
|
||||||
|
// @TODO |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'nex': |
||||||
|
|
||||||
|
// @TODO |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 'gemini': |
||||||
|
|
||||||
|
$request = new \Yggverse\Gemini\Client\Request( |
||||||
|
$address->get() |
||||||
|
); |
||||||
|
|
||||||
|
$response = new \Yggverse\Gemini\Client\Response( |
||||||
|
$request->getResponse() |
||||||
|
); |
||||||
|
|
||||||
|
if (20 === $response->getCode()) |
||||||
|
{ |
||||||
|
switch (true) |
||||||
|
{ |
||||||
|
case str_contains($response->getMeta(), 'text/gemini'): |
||||||
|
|
||||||
|
$title = null; |
||||||
|
|
||||||
|
$this->content->data->setValue( |
||||||
|
$response->getBody(), |
||||||
|
$title |
||||||
|
); |
||||||
|
|
||||||
|
if ($title) // detect title by document h1 |
||||||
|
{ |
||||||
|
$this->title->gtk->set_text( |
||||||
|
$title |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
|
||||||
|
$this->content->data->setValue( |
||||||
|
$response->getBody() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
$this->statusbar->gtk->set_text( |
||||||
|
$response->getMeta() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
else |
||||||
|
{ |
||||||
|
$this->title->gtk->set_text( |
||||||
|
'Failure' |
||||||
|
); |
||||||
|
|
||||||
|
$this->content->data->setValue( |
||||||
|
sprintf( |
||||||
|
'Resource not available (code %d)', |
||||||
|
intval( |
||||||
|
$response->getCode() |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->statusbar->gtk->set_text( |
||||||
|
'Request failed' |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case null: |
||||||
|
|
||||||
|
// Try gemini protocol |
||||||
|
$address = new \Yggverse\Net\Address( |
||||||
|
sprintf( |
||||||
|
'gemini://%s', |
||||||
|
$this->navbar->request->gtk->get_text() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
// Address correct |
||||||
|
if ($address->getHost()) |
||||||
|
{ |
||||||
|
$this->navbar->request->gtk->set_text( |
||||||
|
$address->get() |
||||||
|
); |
||||||
|
|
||||||
|
$this->update(); |
||||||
|
} |
||||||
|
|
||||||
|
// Search request |
||||||
|
else |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
|
||||||
|
default: |
||||||
|
|
||||||
|
$this->title->gtk->set_text( |
||||||
|
'Oops!' |
||||||
|
); |
||||||
|
|
||||||
|
$this->content->data->setValue( |
||||||
|
sprintf( |
||||||
|
'Protocol not supported', |
||||||
|
intval( |
||||||
|
$response->getCode() |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
$this->tab->window->header->setTitle( |
||||||
|
$this->title->gtk->get_text() |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->show_all(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Content\Gemtext; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Content\Plain; |
||||||
|
|
||||||
|
class Content |
||||||
|
{ |
||||||
|
public \GtkScrolledWindow $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address $address; |
||||||
|
|
||||||
|
public Gemtext | Plain $data; |
||||||
|
|
||||||
|
private int $_margin = 8; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\Address $address |
||||||
|
) { |
||||||
|
$this->address = $address; |
||||||
|
|
||||||
|
$this->gtk = new \GtkScrolledWindow; |
||||||
|
|
||||||
|
$this->gtk->set_margin_start( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_end( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->data = new Gemtext( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->data->gtk |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->show_all(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,231 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address\Content; |
||||||
|
|
||||||
|
use \Yggverse\Gemtext\Document; |
||||||
|
use \Yggverse\Net\Address; |
||||||
|
|
||||||
|
class Gemtext |
||||||
|
{ |
||||||
|
public \GtkLabel $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Content $content; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private string $_value = ''; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\Address\Content $content |
||||||
|
) { |
||||||
|
$this->content = $content; |
||||||
|
|
||||||
|
$this->gtk = new \GtkLabel; |
||||||
|
|
||||||
|
$this->gtk->set_use_markup( |
||||||
|
true |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_selectable( |
||||||
|
true |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_line_wrap( |
||||||
|
true |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_xalign( |
||||||
|
0 |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_yalign( |
||||||
|
0 |
||||||
|
); |
||||||
|
|
||||||
|
$this->setValue( |
||||||
|
$this->_value |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->connect( |
||||||
|
'activate-link', |
||||||
|
function( |
||||||
|
\GtkLabel $label, |
||||||
|
string $href |
||||||
|
) { |
||||||
|
$this->content->address->navbar->request->gtk->set_text( |
||||||
|
$this->_url( |
||||||
|
$href |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->content->address->update(); |
||||||
|
} |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function setValue( |
||||||
|
string $value, |
||||||
|
string | null &$title = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$document = new Document( |
||||||
|
$value |
||||||
|
); |
||||||
|
|
||||||
|
$line = []; |
||||||
|
|
||||||
|
foreach ($document->getEntities() as $entity) |
||||||
|
{ |
||||||
|
switch (true) |
||||||
|
{ |
||||||
|
case $entity instanceof \Yggverse\Gemtext\Entity\Code: |
||||||
|
|
||||||
|
if ($entity->isInline()) |
||||||
|
{ |
||||||
|
$line[] = sprintf( |
||||||
|
'<tt>%s</tt>', |
||||||
|
htmlspecialchars( |
||||||
|
$entity->getAlt() |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
else |
||||||
|
{ |
||||||
|
// @TODO multiline |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case $entity instanceof \Yggverse\Gemtext\Entity\Header: |
||||||
|
|
||||||
|
switch ($entity->getLevel()) |
||||||
|
{ |
||||||
|
case 1: // # |
||||||
|
|
||||||
|
$line[] = sprintf( |
||||||
|
'<span size="xx-large">%s</span>', |
||||||
|
htmlspecialchars( |
||||||
|
$entity->getText() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
// Find and return document title by first # tag |
||||||
|
if (empty($title)) |
||||||
|
{ |
||||||
|
$title = $entity->getText(); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 2: // ## |
||||||
|
|
||||||
|
$line[] = sprintf( |
||||||
|
'<span size="x-large">%s</span>', |
||||||
|
htmlspecialchars( |
||||||
|
$entity->getText() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 3: // ### |
||||||
|
|
||||||
|
$line[] = sprintf( |
||||||
|
'<span size="large">%s</span>', |
||||||
|
htmlspecialchars( |
||||||
|
$entity->getText() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
default: |
||||||
|
|
||||||
|
throw new \Exception; |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case $entity instanceof \Yggverse\Gemtext\Entity\Link: |
||||||
|
|
||||||
|
$line[] = sprintf( |
||||||
|
'<a href="%s" title="%s">%s</a>', |
||||||
|
$this->_url( |
||||||
|
$entity->getAddress() |
||||||
|
), |
||||||
|
htmlspecialchars( |
||||||
|
$entity->getAddress() |
||||||
|
), |
||||||
|
htmlspecialchars( |
||||||
|
$entity->getAlt() ? $entity->getAlt() |
||||||
|
: $entity->getAddress() // @TODO date |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case $entity instanceof \Yggverse\Gemtext\Entity\Listing: |
||||||
|
|
||||||
|
$line[] = sprintf( |
||||||
|
'* %s', |
||||||
|
htmlspecialchars( |
||||||
|
$entity->getItem() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case $entity instanceof \Yggverse\Gemtext\Entity\Quote: |
||||||
|
|
||||||
|
$line[] = sprintf( |
||||||
|
'<i>%s</i>', |
||||||
|
htmlspecialchars( |
||||||
|
$entity->getText() |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case $entity instanceof \Yggverse\Gemtext\Entity\Text: |
||||||
|
|
||||||
|
$line[] = htmlspecialchars( |
||||||
|
$entity->getData() |
||||||
|
); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
|
||||||
|
throw new \Exception; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$this->gtk->set_markup( |
||||||
|
implode( |
||||||
|
PHP_EOL, |
||||||
|
$line |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
private function _url( |
||||||
|
string $link |
||||||
|
): ?string |
||||||
|
{ |
||||||
|
$address = new Address( |
||||||
|
$link |
||||||
|
); |
||||||
|
|
||||||
|
if ($address->isRelative()) |
||||||
|
{ |
||||||
|
$address->toAbsolute( |
||||||
|
new Address( |
||||||
|
$this->content->address->navbar->request->gtk->get_text() |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return $address->get(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address\Content; |
||||||
|
|
||||||
|
class Plain |
||||||
|
{ |
||||||
|
public \GtkLabel $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Content $content; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private string $_value = ''; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\Address\Content $content |
||||||
|
) { |
||||||
|
$this->content = $content; |
||||||
|
|
||||||
|
$this->gtk = new \GtkLabel( |
||||||
|
$this->_value |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_use_markup( |
||||||
|
false |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_selectable( |
||||||
|
true |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_line_wrap( |
||||||
|
true |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_xalign( |
||||||
|
0 |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_yalign( |
||||||
|
0 |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function setValue( |
||||||
|
string $value |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->gtk->set_text( |
||||||
|
$value |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\Base; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\Go; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\History; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\Request; |
||||||
|
|
||||||
|
class Navbar |
||||||
|
{ |
||||||
|
public \GtkBox $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address $address; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\Base $base; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\Go $go; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\History $history; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\Request $request; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private int $_margin = 8; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\Address $address |
||||||
|
) { |
||||||
|
$this->address = $address; |
||||||
|
|
||||||
|
// Init navbar area |
||||||
|
$this->gtk = new \GtkBox( |
||||||
|
\GtkOrientation::HORIZONTAL |
||||||
|
); |
||||||
|
|
||||||
|
$this->setMargins( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
// Append base button |
||||||
|
$this->base = new Base( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->base->gtk |
||||||
|
); |
||||||
|
|
||||||
|
// Append history buttons group |
||||||
|
$this->history = new History( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->history->gtk |
||||||
|
); |
||||||
|
|
||||||
|
// Append request entry, fill empty space |
||||||
|
$this->request = new Request( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->pack_start( |
||||||
|
$this->request->gtk, |
||||||
|
true, |
||||||
|
true, |
||||||
|
0 |
||||||
|
); |
||||||
|
|
||||||
|
// Append go button |
||||||
|
$this->go = new Go( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->go->gtk |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function setMargins( |
||||||
|
?int $value |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->gtk->set_margin_top( |
||||||
|
$value ?? $this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_bottom( |
||||||
|
$value ?? $this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_start( |
||||||
|
$value ?? $this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_end( |
||||||
|
$value ?? $this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_spacing( |
||||||
|
$value ?? $this->_margin |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address\Navbar; |
||||||
|
|
||||||
|
class Base extends \Yggverse\Yoda\Abstract\Entity\Window\Tab\Address\Navbar\Button |
||||||
|
{ |
||||||
|
protected string $_label = 'Base'; |
||||||
|
|
||||||
|
protected function _onCLick( |
||||||
|
\GtkButton $entity |
||||||
|
): void |
||||||
|
{ |
||||||
|
$address = new \Yggverse\Net\Address( |
||||||
|
$this->navbar->request->gtk->get_text() |
||||||
|
); |
||||||
|
|
||||||
|
if ($address->getHost()) |
||||||
|
{ |
||||||
|
$this->navbar->request->gtk->set_text( |
||||||
|
$address->get( // build base |
||||||
|
true, |
||||||
|
true, |
||||||
|
true, |
||||||
|
true, |
||||||
|
true, |
||||||
|
false, |
||||||
|
false, |
||||||
|
false |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->navbar->address->update(); |
||||||
|
} |
||||||
|
|
||||||
|
$this->update(); |
||||||
|
} |
||||||
|
|
||||||
|
public function update( |
||||||
|
?\Yggverse\Net\Address $address = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
if (is_null($address)) |
||||||
|
{ |
||||||
|
$address = new \Yggverse\Net\Address( |
||||||
|
$this->navbar->request->gtk->get_text() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
$this->navbar->base->gtk->set_sensitive( |
||||||
|
$address->getHost() && ($address->getPath() || $address->getQuery()) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address\Navbar; |
||||||
|
|
||||||
|
class Go extends \Yggverse\Yoda\Abstract\Entity\Window\Tab\Address\Navbar\Button |
||||||
|
{ |
||||||
|
protected string $_label = 'Go'; |
||||||
|
|
||||||
|
protected function _onCLick( |
||||||
|
\GtkButton $entity |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->navbar->address->update(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address\Navbar; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\History\Back; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\History\Forward; |
||||||
|
|
||||||
|
class History |
||||||
|
{ |
||||||
|
public \GtkButtonBox $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar $navbar; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\History\Back $back; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\History\Forward $forward; |
||||||
|
|
||||||
|
private \Yggverse\Yoda\Model\History $_history; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\Address\Navbar $navbar |
||||||
|
) { |
||||||
|
$this->_history = new \Yggverse\Yoda\Model\History(); |
||||||
|
|
||||||
|
$this->navbar = $navbar; |
||||||
|
|
||||||
|
$this->gtk = new \GtkButtonBox( |
||||||
|
\GtkOrientation::HORIZONTAL |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_layout( |
||||||
|
\GtkButtonBoxStyle::EXPAND |
||||||
|
); |
||||||
|
|
||||||
|
$this->back = new Back( |
||||||
|
$this->navbar |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->back->gtk |
||||||
|
); |
||||||
|
|
||||||
|
$this->forward = new Forward( |
||||||
|
$this->navbar |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->forward->gtk |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function add( |
||||||
|
string $url |
||||||
|
): void |
||||||
|
{ |
||||||
|
if (empty($url)) |
||||||
|
{ |
||||||
|
throw new \Exception; |
||||||
|
} |
||||||
|
|
||||||
|
if ($url != $this->_history->getCurrent()) |
||||||
|
{ |
||||||
|
$this->_history->add( |
||||||
|
$url |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
$this->back->gtk->set_sensitive( |
||||||
|
(bool) $this->_history->getBack() |
||||||
|
); |
||||||
|
|
||||||
|
$this->forward->gtk->set_sensitive( |
||||||
|
(bool) $this->_history->getForward() |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\History; |
||||||
|
|
||||||
|
class Back extends \Yggverse\Yoda\Abstract\Entity\Window\Tab\Address\Navbar\Button |
||||||
|
{ |
||||||
|
protected string $_label = 'Back'; |
||||||
|
|
||||||
|
protected function _onCLick( |
||||||
|
\GtkButton $entity |
||||||
|
): void |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address\Navbar\History; |
||||||
|
|
||||||
|
class Forward extends \Yggverse\Yoda\Abstract\Entity\Window\Tab\Address\Navbar\Button |
||||||
|
{ |
||||||
|
protected string $_label = 'Forward'; |
||||||
|
|
||||||
|
protected function _onCLick( |
||||||
|
\GtkButton $entity |
||||||
|
): void |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address\Navbar; |
||||||
|
|
||||||
|
class Request extends \Yggverse\Yoda\Abstract\Entity\Window\Tab\Address\Navbar\Entry |
||||||
|
{ |
||||||
|
private string $_placeholder = 'URL or search term...'; |
||||||
|
|
||||||
|
protected function _onActivate( |
||||||
|
\GtkEntry $entry |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->navbar->address->update(); |
||||||
|
} |
||||||
|
|
||||||
|
protected function _onKeyRelease( |
||||||
|
\GtkEntry $entry, |
||||||
|
\GdkEvent $event |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->navbar->base->update(); |
||||||
|
|
||||||
|
$this->navbar->go->gtk->set_sensitive( |
||||||
|
!empty( |
||||||
|
$entry->get_text() |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address; |
||||||
|
|
||||||
|
class Statusbar |
||||||
|
{ |
||||||
|
public \GtkLabel $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address $address; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private int $_margin = 8; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\Address $address |
||||||
|
) { |
||||||
|
$this->address = $address; |
||||||
|
|
||||||
|
$this->gtk = new \GtkLabel; |
||||||
|
|
||||||
|
$this->gtk->set_line_wrap( |
||||||
|
true |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_xalign( |
||||||
|
0 |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_yalign( |
||||||
|
0 |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_top( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_bottom( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_start( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_end( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\Address; |
||||||
|
|
||||||
|
class Title |
||||||
|
{ |
||||||
|
public \GtkLabel $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\Address $address; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private int $_ellipsize = 3; |
||||||
|
private int $_length = 12; |
||||||
|
private string $_value = 'New address'; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\Address $address, |
||||||
|
) { |
||||||
|
$this->address = $address; |
||||||
|
|
||||||
|
$this->gtk = new \GtkLabel( |
||||||
|
$this->_value |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_width_chars( |
||||||
|
$this->_length |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_ellipsize( |
||||||
|
$this->_ellipsize |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\History\Title; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\History\Navbar; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\History\Content; |
||||||
|
|
||||||
|
class History |
||||||
|
{ |
||||||
|
public \GtkBox $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab $tab; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History\Title $title; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History\Navbar $navbar; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History\Content $content; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab $tab |
||||||
|
) { |
||||||
|
$this->tab = $tab; |
||||||
|
|
||||||
|
$this->title = new Title( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk = new \GtkBox( |
||||||
|
\GtkOrientation::VERTICAL |
||||||
|
); |
||||||
|
|
||||||
|
$this->content = new Content( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->navbar = new Navbar( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->navbar->gtk |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->pack_start( |
||||||
|
$this->content->gtk, |
||||||
|
true, |
||||||
|
true, |
||||||
|
0 |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,220 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\History; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address; |
||||||
|
|
||||||
|
class Content |
||||||
|
{ |
||||||
|
public \GtkScrolledWindow $gtk; |
||||||
|
|
||||||
|
public \GtkTreeView $treeview; |
||||||
|
public \GtkListStore $list; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History $history; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private string $_time = 'Time'; |
||||||
|
private string $_title = 'Title'; |
||||||
|
private string $_url = 'URL'; |
||||||
|
private string $_format = 'c'; |
||||||
|
private int $_margin = 8; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\History $history |
||||||
|
) { |
||||||
|
$this->history = $history; |
||||||
|
|
||||||
|
$this->gtk = new \GtkScrolledWindow; |
||||||
|
|
||||||
|
$this->gtk->set_margin_start( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_end( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->treeview = new \GtkTreeView; |
||||||
|
|
||||||
|
$this->treeview->append_column( |
||||||
|
new \GtkTreeViewColumn( |
||||||
|
$this->_time, |
||||||
|
new \GtkCellRendererText(), |
||||||
|
'text', |
||||||
|
1 |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->treeview->append_column( |
||||||
|
new \GtkTreeViewColumn( |
||||||
|
$this->_url, |
||||||
|
new \GtkCellRendererText(), |
||||||
|
'text', |
||||||
|
2 |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->treeview->append_column( |
||||||
|
new \GtkTreeViewColumn( |
||||||
|
$this->_title, |
||||||
|
new \GtkCellRendererText(), |
||||||
|
'text', |
||||||
|
3 |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
$this->list = new \GtkListStore( |
||||||
|
\GObject::TYPE_INT, |
||||||
|
\GObject::TYPE_STRING, |
||||||
|
\GObject::TYPE_STRING, |
||||||
|
\GObject::TYPE_STRING |
||||||
|
); |
||||||
|
|
||||||
|
$this->treeview->set_model( |
||||||
|
$this->list |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->treeview |
||||||
|
); |
||||||
|
|
||||||
|
$this->search(); |
||||||
|
|
||||||
|
$this->treeview->connect( |
||||||
|
'row-activated', |
||||||
|
function( |
||||||
|
\GtkTreeView $treeview |
||||||
|
) { |
||||||
|
$address = new Address( |
||||||
|
$this->history->tab |
||||||
|
); |
||||||
|
|
||||||
|
$address->navbar->request->gtk->set_text( |
||||||
|
$this->getSelectedUrl() |
||||||
|
); |
||||||
|
|
||||||
|
$this->history->tab->append( |
||||||
|
$address |
||||||
|
); |
||||||
|
|
||||||
|
$address->update(); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
$this->treeview->connect( |
||||||
|
'cursor-changed', |
||||||
|
function( |
||||||
|
\GtkTreeView $treeview |
||||||
|
) { |
||||||
|
$this->history->navbar->open->gtk->set_sensitive( |
||||||
|
(bool) $this->getSelectedId() |
||||||
|
); |
||||||
|
|
||||||
|
$this->history->navbar->delete->gtk->set_sensitive( |
||||||
|
(bool) $this->getSelectedId() |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function append( |
||||||
|
int $id, |
||||||
|
int $time, |
||||||
|
string $url, |
||||||
|
?string $title |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->list->append( |
||||||
|
[ |
||||||
|
$id, |
||||||
|
date( |
||||||
|
$this->_format, |
||||||
|
$time |
||||||
|
), |
||||||
|
$url, |
||||||
|
strval( |
||||||
|
$title |
||||||
|
) |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function clear(): void |
||||||
|
{ |
||||||
|
$this->list->clear(); |
||||||
|
} |
||||||
|
|
||||||
|
public function search( |
||||||
|
string $filter = '' |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->clear(); |
||||||
|
|
||||||
|
if ($records = $this->history->tab->window->database->findHistory($filter)) |
||||||
|
{ |
||||||
|
foreach ($records as $record) |
||||||
|
{ |
||||||
|
$this->append( |
||||||
|
$record->id, |
||||||
|
$record->time, |
||||||
|
$record->url, |
||||||
|
$record->title |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
else |
||||||
|
{ |
||||||
|
$this->history->navbar->open->gtk->set_sensitive( |
||||||
|
false |
||||||
|
); |
||||||
|
|
||||||
|
$this->history->navbar->delete->gtk->set_sensitive( |
||||||
|
false |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function getSelectedId(): ?int |
||||||
|
{ |
||||||
|
if ($id = $this->_getSelected(0)) |
||||||
|
{ |
||||||
|
return $id; |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public function getSelectedUrl(): ?string |
||||||
|
{ |
||||||
|
if ($url = $this->_getSelected(2)) |
||||||
|
{ |
||||||
|
return $url; |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private function _getSelected( |
||||||
|
int $column |
||||||
|
): null|int|string |
||||||
|
{ |
||||||
|
list( |
||||||
|
$list, |
||||||
|
$row |
||||||
|
) = $this->treeview->get_selection()->get_selected(); |
||||||
|
|
||||||
|
if ($list && $row) |
||||||
|
{ |
||||||
|
if ($value = $list->get_value($row, $column)) |
||||||
|
{ |
||||||
|
return $value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,99 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\History; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\History\Navbar\Delete; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\History\Navbar\Filter; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\History\Navbar\Open; |
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\History\Navbar\Search; |
||||||
|
|
||||||
|
class Navbar |
||||||
|
{ |
||||||
|
public \GtkBox $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History $history; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History\Navbar\Delete $delete; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History\Navbar\Filter $filter; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History\Navbar\Open $open; |
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History\Navbar\Search $search; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private int $_margin = 8; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\History $history |
||||||
|
) { |
||||||
|
$this->history = $history; |
||||||
|
|
||||||
|
$this->gtk = new \GtkBox( |
||||||
|
\GtkOrientation::HORIZONTAL |
||||||
|
); |
||||||
|
|
||||||
|
$this->setMargin( |
||||||
|
$this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->open = new Open( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->open->gtk |
||||||
|
); |
||||||
|
|
||||||
|
$this->delete = new Delete( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->delete->gtk |
||||||
|
); |
||||||
|
|
||||||
|
$this->filter = new Filter( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->pack_start( |
||||||
|
$this->filter->gtk, |
||||||
|
true, |
||||||
|
true, |
||||||
|
0 |
||||||
|
); |
||||||
|
|
||||||
|
$this->search = new Search( |
||||||
|
$this |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->add( |
||||||
|
$this->search->gtk |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function setMargin( |
||||||
|
?int $value = null |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->gtk->set_margin_top( |
||||||
|
$margin ?? $this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_bottom( |
||||||
|
$margin ?? $this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_start( |
||||||
|
$margin ?? $this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_margin_end( |
||||||
|
$margin ?? $this->_margin |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_spacing( |
||||||
|
$margin ?? $this->_margin |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\History\Navbar; |
||||||
|
|
||||||
|
class Delete extends \Yggverse\Yoda\Abstract\Entity\Window\Tab\History\Navbar\Button |
||||||
|
{ |
||||||
|
protected string $_label = 'Delete'; |
||||||
|
|
||||||
|
protected function _onCLick( |
||||||
|
\GtkButton $entity |
||||||
|
): void |
||||||
|
{ |
||||||
|
if ($id = $this->navbar->history->content->getSelectedId()) |
||||||
|
{ |
||||||
|
$this->navbar->history->tab->window->database->deleteHistory( |
||||||
|
$id |
||||||
|
); |
||||||
|
|
||||||
|
$this->navbar->open->gtk->set_sensitive( |
||||||
|
false |
||||||
|
); |
||||||
|
|
||||||
|
$this->navbar->history->content->search( |
||||||
|
$this->navbar->filter->gtk->get_text() |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\History\Navbar; |
||||||
|
|
||||||
|
class Filter extends \Yggverse\Yoda\Abstract\Entity\Window\Tab\History\Navbar\Entry |
||||||
|
{ |
||||||
|
private string $_placeholder = 'Search in history...'; |
||||||
|
|
||||||
|
protected function _onActivate( |
||||||
|
\GtkEntry $entry |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->navbar->history->content->search( |
||||||
|
$this->navbar->filter->gtk->get_text() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
protected function _onKeyRelease( |
||||||
|
\GtkEntry $entry, |
||||||
|
\GdkEvent $event |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->navbar->history->content->search( |
||||||
|
$this->navbar->filter->gtk->get_text() |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\History\Navbar; |
||||||
|
|
||||||
|
use \Yggverse\Yoda\Entity\Window\Tab\Address; |
||||||
|
|
||||||
|
class Open extends \Yggverse\Yoda\Abstract\Entity\Window\Tab\History\Navbar\Button |
||||||
|
{ |
||||||
|
protected string $_label = 'Open'; |
||||||
|
|
||||||
|
protected function _onCLick( |
||||||
|
\GtkButton $entity |
||||||
|
): void |
||||||
|
{ |
||||||
|
$address = new Address( |
||||||
|
$this->navbar->history->tab |
||||||
|
); |
||||||
|
|
||||||
|
$this->navbar->history->tab->append( // @TODO |
||||||
|
$address |
||||||
|
); |
||||||
|
|
||||||
|
$address->navbar->request->gtk->set_text( |
||||||
|
$this->navbar->history->content->getSelectedUrl() |
||||||
|
); |
||||||
|
|
||||||
|
$address->update(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\History\Navbar; |
||||||
|
|
||||||
|
class Search extends \Yggverse\Yoda\Abstract\Entity\Window\Tab\History\Navbar\Button |
||||||
|
{ |
||||||
|
protected bool $_sensitive = true; |
||||||
|
protected string $_label = 'Search'; |
||||||
|
|
||||||
|
protected function _onCLick( |
||||||
|
\GtkButton $entity |
||||||
|
): void |
||||||
|
{ |
||||||
|
$this->gtk->set_sensitive( |
||||||
|
false |
||||||
|
); |
||||||
|
|
||||||
|
$this->navbar->history->content->search( |
||||||
|
$this->navbar->filter->gtk->get_text() |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_sensitive( |
||||||
|
true |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Yoda\Entity\Window\Tab\History; |
||||||
|
|
||||||
|
class Title |
||||||
|
{ |
||||||
|
public \GtkLabel $gtk; |
||||||
|
|
||||||
|
public \Yggverse\Yoda\Entity\Window\Tab\History $history; |
||||||
|
|
||||||
|
// Defaults |
||||||
|
private int $_ellipsize = 0; |
||||||
|
private int $_length = 12; |
||||||
|
private string $_value = 'History'; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Yoda\Entity\Window\Tab\History $history |
||||||
|
) { |
||||||
|
$this->history = $history; |
||||||
|
|
||||||
|
$this->gtk = new \GtkLabel( |
||||||
|
$this->_value |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_width_chars( |
||||||
|
$this->_length |
||||||
|
); |
||||||
|
|
||||||
|
$this->gtk->set_ellipsize( |
||||||
|
$this->_ellipsize |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -1,45 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace Yggverse\Yoda\Model; |
|
||||||
|
|
||||||
class Config |
|
||||||
{ |
|
||||||
public function __construct( |
|
||||||
?string $filename = null |
|
||||||
) { |
|
||||||
if (empty($filename)) |
|
||||||
{ |
|
||||||
$filename = __DIR__ . |
|
||||||
DIRECTORY_SEPARATOR . '..' . |
|
||||||
DIRECTORY_SEPARATOR . '..' . |
|
||||||
DIRECTORY_SEPARATOR . 'config.json'; |
|
||||||
} |
|
||||||
|
|
||||||
if (!file_exists($filename)) |
|
||||||
{ |
|
||||||
throw new \Exception; // @TODO |
|
||||||
} |
|
||||||
|
|
||||||
if (!is_readable($filename)) |
|
||||||
{ |
|
||||||
throw new \Exception; // @TODO |
|
||||||
} |
|
||||||
|
|
||||||
if (!$data = file_get_contents($filename)) |
|
||||||
{ |
|
||||||
throw new \Exception; // @TODO |
|
||||||
} |
|
||||||
|
|
||||||
if (!$config = @json_decode($data)) |
|
||||||
{ |
|
||||||
throw new \Exception; // @TODO |
|
||||||
} |
|
||||||
|
|
||||||
foreach ($config as $key => $value) |
|
||||||
{ |
|
||||||
$this->{$key} = $value; // @TODO |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,30 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace Yggverse\Yoda\Model; |
|
||||||
|
|
||||||
class File |
|
||||||
{ |
|
||||||
public static function getTheme(string $name): string |
|
||||||
{ |
|
||||||
$filename = __DIR__ . |
|
||||||
DIRECTORY_SEPARATOR . '..' . |
|
||||||
DIRECTORY_SEPARATOR . 'Theme' . |
|
||||||
DIRECTORY_SEPARATOR . $name . '.css'; |
|
||||||
|
|
||||||
if (file_exists($filename) && is_readable($filename)) |
|
||||||
{ |
|
||||||
$result = file_get_contents( |
|
||||||
$filename |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
if (empty($result)) |
|
||||||
{ |
|
||||||
throw new \Exception(); // @TODO |
|
||||||
} |
|
||||||
|
|
||||||
return $result; |
|
||||||
} |
|
||||||
} |
|
@ -1,33 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace Yggverse\Yoda\Model; |
|
||||||
|
|
||||||
class Memory |
|
||||||
{ |
|
||||||
private array $_memory = []; |
|
||||||
|
|
||||||
public function __construct() |
|
||||||
{} |
|
||||||
|
|
||||||
public function set(string $key, mixed $value): void |
|
||||||
{ |
|
||||||
$this->_memory[$key] = $value; |
|
||||||
} |
|
||||||
|
|
||||||
public function get(string $key): mixed |
|
||||||
{ |
|
||||||
if (isset($this->_memory[$key])) |
|
||||||
{ |
|
||||||
return $this->_memory[$key]; |
|
||||||
} |
|
||||||
|
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
public function flush(): void |
|
||||||
{ |
|
||||||
$this->_memory = []; |
|
||||||
} |
|
||||||
} |
|
@ -1,31 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace Yggverse\Yoda\Model; |
|
||||||
|
|
||||||
class Page |
|
||||||
{ |
|
||||||
public static function get(string $name): ?string |
|
||||||
{ |
|
||||||
$name = ucfirst( |
|
||||||
mb_strtolower( |
|
||||||
$name |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$filename = __DIR__ . |
|
||||||
DIRECTORY_SEPARATOR . '..' . |
|
||||||
DIRECTORY_SEPARATOR . 'Page' . |
|
||||||
DIRECTORY_SEPARATOR . $name . '.gmi'; |
|
||||||
|
|
||||||
if (file_exists($filename) && is_readable($filename)) |
|
||||||
{ |
|
||||||
return file_get_contents( |
|
||||||
$filename |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
@ -1,3 +0,0 @@ |
|||||||
# Not found |
|
||||||
|
|
||||||
Requested resource not available |
|
@ -1,5 +0,0 @@ |
|||||||
# Oops! |
|
||||||
|
|
||||||
Something went wrong.. |
|
||||||
|
|
||||||
=> https://github.com/YGGverse/Yoda/issues Report |
|
@ -1,8 +0,0 @@ |
|||||||
# Protocol issue |
|
||||||
|
|
||||||
At this moment, supported protocols: |
|
||||||
|
|
||||||
* gemini |
|
||||||
* yoda |
|
||||||
|
|
||||||
=> https://github.com/YGGverse/Yoda/issues Report |
|
@ -1,8 +0,0 @@ |
|||||||
# Redirect issue |
|
||||||
|
|
||||||
You see this message because page redirect could not be processed properly |
|
||||||
|
|
||||||
## Possible reasons |
|
||||||
|
|
||||||
* Max redirects reached |
|
||||||
* Redirects disabled by settings |
|
@ -1,3 +0,0 @@ |
|||||||
# Welcome to Yoda! |
|
||||||
|
|
||||||
=> https://github.com/YGGverse/Yoda |
|
Loading…
Reference in new issue