mirror of https://github.com/YGGverse/Yoda.git
yggverse
5 months ago
47 changed files with 1927 additions and 2069 deletions
@ -1,28 +1,24 @@
@@ -1,28 +1,24 @@
|
||||
# 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 |
||||
* [x] Nex |
||||
1. Build latest [PHP-GTK3](https://github.com/scorninpc/php-gtk3) from sources or get [Appimage](https://github.com/scorninpc/php-gtk3/releases) |
||||
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)) |
||||
* [x] Flexible settings in `config.json`, then UI |
||||
* [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)) |
||||
``` bash |
||||
/path/to/php-gtk3 src/Yoda.php |
||||
``` |
||||
|
||||
## Components |
||||
|
||||
* [gemini-php](https://github.com/YGGverse/gemini-php) - PHP 8 library for Gemini protocol |
||||
* [gemtext-php](https://github.com/YGGverse/gemtext-php) - PHP 8 library for Gemtext operations |
||||
* [net-php](https://github.com/YGGverse/net-php) - PHP 8 library for DNS resolver and address parser |
||||
* [gemini-php](https://github.com/YGGverse/gemini-php) - Gemini protocol connections |
||||
* [gemtext-php](https://github.com/YGGverse/gemtext-php) - Gemtext operations |
||||
* [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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -1,3 +0,0 @@
|
||||
# Not found |
||||
|
||||
Requested resource not available |
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
# Oops! |
||||
|
||||
Something went wrong.. |
||||
|
||||
=> https://github.com/YGGverse/Yoda/issues Report |
@ -1,8 +0,0 @@
@@ -1,8 +0,0 @@
|
||||
# Protocol issue |
||||
|
||||
At this moment, supported protocols: |
||||
|
||||
* gemini |
||||
* yoda |
||||
|
||||
=> https://github.com/YGGverse/Yoda/issues Report |
@ -1,8 +0,0 @@
@@ -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 @@
@@ -1,3 +0,0 @@
|
||||
# Welcome to Yoda! |
||||
|
||||
=> https://github.com/YGGverse/Yoda |
Loading…
Reference in new issue