mirror of
https://github.com/YGGverse/Yoda.git
synced 2026-03-31 16:45:27 +00:00
refactor to separated entities, init abstractions
This commit is contained in:
parent
08c9fe76e5
commit
90acc3ac2d
47 changed files with 1927 additions and 2069 deletions
34
README.md
34
README.md
|
|
@ -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
|
||||||
227
config.json
227
config.json
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
41
src/Abstract/Entity/Button.php
Normal file
41
src/Abstract/Entity/Button.php
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
64
src/Abstract/Entity/Entry.php
Normal file
64
src/Abstract/Entity/Entry.php
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
18
src/Abstract/Entity/Window/Tab/Address/Navbar/Button.php
Normal file
18
src/Abstract/Entity/Window/Tab/Address/Navbar/Button.php
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Abstract/Entity/Window/Tab/Address/Navbar/Entry.php
Normal file
18
src/Abstract/Entity/Window/Tab/Address/Navbar/Entry.php
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Abstract/Entity/Window/Tab/History/Navbar/Button.php
Normal file
18
src/Abstract/Entity/Window/Tab/History/Navbar/Button.php
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Abstract/Entity/Window/Tab/History/Navbar/Entry.php
Normal file
18
src/Abstract/Entity/Window/Tab/History/Navbar/Entry.php
Normal file
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
53
src/Entity/Window.php
Normal file
53
src/Entity/Window.php
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/Entity/Window/Header.php
Normal file
45
src/Entity/Window/Header.php
Normal file
|
|
@ -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
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/Entity/Window/Tab.php
Normal file
88
src/Entity/Window/Tab.php
Normal file
|
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
228
src/Entity/Window/Tab/Address.php
Normal file
228
src/Entity/Window/Tab/Address.php
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/Entity/Window/Tab/Address/Content.php
Normal file
45
src/Entity/Window/Tab/Address/Content.php
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
231
src/Entity/Window/Tab/Address/Content/Gemtext.php
Normal file
231
src/Entity/Window/Tab/Address/Content/Gemtext.php
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/Entity/Window/Tab/Address/Content/Plain.php
Normal file
54
src/Entity/Window/Tab/Address/Content/Plain.php
Normal file
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
104
src/Entity/Window/Tab/Address/Navbar.php
Normal file
104
src/Entity/Window/Tab/Address/Navbar.php
Normal file
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/Entity/Window/Tab/Address/Navbar/Base.php
Normal file
55
src/Entity/Window/Tab/Address/Navbar/Base.php
Normal file
|
|
@ -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())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/Entity/Window/Tab/Address/Navbar/Go.php
Normal file
17
src/Entity/Window/Tab/Address/Navbar/Go.php
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/Entity/Window/Tab/Address/Navbar/History.php
Normal file
76
src/Entity/Window/Tab/Address/Navbar/History.php
Normal file
|
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/Entity/Window/Tab/Address/Navbar/History/Back.php
Normal file
17
src/Entity/Window/Tab/Address/Navbar/History/Back.php
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/Entity/Window/Tab/Address/Navbar/History/Forward.php
Normal file
17
src/Entity/Window/Tab/Address/Navbar/History/Forward.php
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/Entity/Window/Tab/Address/Navbar/Request.php
Normal file
31
src/Entity/Window/Tab/Address/Navbar/Request.php
Normal file
|
|
@ -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()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/Entity/Window/Tab/Address/Statusbar.php
Normal file
51
src/Entity/Window/Tab/Address/Statusbar.php
Normal file
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/Entity/Window/Tab/Address/Title.php
Normal file
35
src/Entity/Window/Tab/Address/Title.php
Normal file
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/Entity/Window/Tab/History.php
Normal file
53
src/Entity/Window/Tab/History.php
Normal file
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
220
src/Entity/Window/Tab/History/Content.php
Normal file
220
src/Entity/Window/Tab/History/Content.php
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/Entity/Window/Tab/History/Navbar.php
Normal file
99
src/Entity/Window/Tab/History/Navbar.php
Normal file
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/Entity/Window/Tab/History/Navbar/Delete.php
Normal file
30
src/Entity/Window/Tab/History/Navbar/Delete.php
Normal file
|
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/Entity/Window/Tab/History/Navbar/Filter.php
Normal file
29
src/Entity/Window/Tab/History/Navbar/Filter.php
Normal file
|
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/Entity/Window/Tab/History/Navbar/Open.php
Normal file
31
src/Entity/Window/Tab/History/Navbar/Open.php
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/Entity/Window/Tab/History/Navbar/Search.php
Normal file
28
src/Entity/Window/Tab/History/Navbar/Search.php
Normal file
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/Entity/Window/Tab/History/Title.php
Normal file
35
src/Entity/Window/Tab/History/Title.php
Normal file
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -64,8 +64,8 @@ class Database
|
||||||
return (int) $this->_database->lastInsertId();
|
return (int) $this->_database->lastInsertId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHistory(
|
public function findHistory(
|
||||||
string $search = '',
|
string $value = '',
|
||||||
int $start = 0,
|
int $start = 0,
|
||||||
int $limit = 1000
|
int $limit = 1000
|
||||||
): array
|
): array
|
||||||
|
|
@ -73,7 +73,7 @@ class Database
|
||||||
$query = $this->_database->prepare(
|
$query = $this->_database->prepare(
|
||||||
sprintf(
|
sprintf(
|
||||||
'SELECT * FROM `history`
|
'SELECT * FROM `history`
|
||||||
WHERE `url` LIKE :search OR `title` LIKE :search
|
WHERE `url` LIKE :value OR `title` LIKE :value
|
||||||
ORDER BY `id` DESC
|
ORDER BY `id` DESC
|
||||||
LIMIT %d,%d',
|
LIMIT %d,%d',
|
||||||
$start,
|
$start,
|
||||||
|
|
@ -83,9 +83,9 @@ class Database
|
||||||
|
|
||||||
$query->execute(
|
$query->execute(
|
||||||
[
|
[
|
||||||
':search' => sprintf(
|
':value' => sprintf(
|
||||||
'%%%s%%',
|
'%%%s%%',
|
||||||
$search
|
$value
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
@ -122,4 +122,35 @@ class Database
|
||||||
|
|
||||||
return $query->rowCount();
|
return $query->rowCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function refreshHistory(
|
||||||
|
string $url,
|
||||||
|
?string $title = null
|
||||||
|
): void
|
||||||
|
{
|
||||||
|
// Find same records match URL
|
||||||
|
$query = $this->_database->prepare(
|
||||||
|
'SELECT * FROM `history` WHERE `url` LIKE :url'
|
||||||
|
);
|
||||||
|
|
||||||
|
$query->execute(
|
||||||
|
[
|
||||||
|
':url' => $url
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Drop previous records
|
||||||
|
foreach ($query->fetchAll() as $record)
|
||||||
|
{
|
||||||
|
$this->deleteHistory(
|
||||||
|
$record->id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new record
|
||||||
|
$this->addHistory(
|
||||||
|
$url,
|
||||||
|
$title
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
/* Custom CSS */
|
|
||||||
29
src/Yoda.php
29
src/Yoda.php
|
|
@ -6,9 +6,34 @@ require_once __DIR__ .
|
||||||
DIRECTORY_SEPARATOR . 'vendor' .
|
DIRECTORY_SEPARATOR . 'vendor' .
|
||||||
DIRECTORY_SEPARATOR . 'autoload.php';
|
DIRECTORY_SEPARATOR . 'autoload.php';
|
||||||
|
|
||||||
// Init app
|
// Init filesystem
|
||||||
|
$filesystem = new \Yggverse\Yoda\Model\Filesystem(
|
||||||
|
(
|
||||||
|
getenv('HOME') ?? __DIR__ . DIRECTORY_SEPARATOR . '..'
|
||||||
|
) . DIRECTORY_SEPARATOR . '.yoda'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Init database
|
||||||
|
$database = new \Yggverse\Yoda\Model\Database(
|
||||||
|
$filesystem->getAbsolute(
|
||||||
|
'database.sqlite'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Init GTK
|
||||||
\Gtk::init();
|
\Gtk::init();
|
||||||
|
|
||||||
new \Yggverse\Yoda\Entity\App;
|
// Init window
|
||||||
|
$window = new \Yggverse\Yoda\Entity\Window(
|
||||||
|
$database
|
||||||
|
);
|
||||||
|
|
||||||
|
$window->gtk->connect(
|
||||||
|
'destroy',
|
||||||
|
function()
|
||||||
|
{
|
||||||
|
\Gtk::main_quit();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
\Gtk::main();
|
\Gtk::main();
|
||||||
Loading…
Add table
Add a link
Reference in a new issue