05 β Menyu, tray va global yorliqlar¶
β¬ οΈ Oldingi: 04 β Oyna boshqaruvi (Window) Β· π README Β· Keyingi: 06 β Native UI: Notification, Dialog, Alert β‘οΈ
Bu bobda: desktop ilovangizga tirik tegadigan to'rt narsani o'rganamiz β yuqoridagi ilova menyusi (Fayl, Tahrir, Ko'rish...), sichqonchaning o'ng tugmasi ostidagi kontekst menyu, doim ko'z oldida turadigan tray/MenuBar ilova (fonda ishlab, ikonka orqali ochiladigan kichik oyna), va butun tizim bo'ylab ishlaydigan global klaviatura yorliqlari (masalan
Ctrl+Shift+Spacebilan tezkor qidiruvni chaqirish). Aniq facade'lar:Menu,MenuBar,ContextMenu,GlobalShortcutβ barchasiNative\Desktop\Facades\...namespace'ida. Har bir metodni rasmiy docs va o'rnatilganvendor/nativephp/desktopmanba kodi bilan tasdiqladik.Halol eslatma: NativePHP sof native widget yaratmaydi β ilovangiz Electron qobiq ichida ishlaydi, UI esa webview'dagi Blade/Livewire. Lekin menyu va tray ikonkasi HAQIQIY native: ularni operatsion tizimning o'zi chizadi (webview emas). PHP faqat menyuning tavsifini (label, hotkey, qaysi event) yuboradi, Electron uni
Menu.buildFromTemplateorqali OS menyusiga aylantiradi. Shu bobdagi Laravel/PHP kod (builder'lar, event'lar, listener'lar)php -lva tirik Laravel boot bilan tekshirilgan; menyu ekranda qanday ko'rinishi (rasmlar) vanative:devGUI bloklari esa illustrativ β bu muhitda displey/Electron yo'q, shuning uchun ishga tushirilmagan.
Kirish: nega menyu va tray muhim¶
Brauzer ilovasi va desktop ilovasini farqlaydigan narsa β aynan shu mayda detallar. Foydalanuvchi Cmd+, bosib sozlamalarni ochishni, ekran tepasida "Fayl" menyusini ko'rishni, tray ikonkasiga bosib ilovani chaqirishni kutadi. Web ilova bularning hech birini bera olmaydi.
NativePHP bu native elementlarni PHP'dan boshqarish imkonini beradi. Yodda tuting (01-bobda chuqur tushuntirilgan): ilovangizning UI'si webview ichidagi HTML, biznes-mantiq PHP, lekin menyu OS darajasida β uni Electron asosiy jarayoni (Node) yaratadi. Demak menyu PHP'da tasvirlanadi, keyin bridge orqali Electron'ga uzatiladi.
Bu bobdagi kodni biz ko'pincha App\Providers\NativeAppServiceProvider ning boot() metodiga yozamiz β bu NativePHP o'rnatilganda (php artisan native:install) yaratiladigan maxsus provayder. Ilova ishga tushganda menyular va yorliqlar shu yerda sozlanadi.
Eslatma: Laravel asoslarini (service provider, event, listener, route) bilasiz deb faraz qilamiz. Esdan chiqargan bo'lsangiz: Laravel qo'llanmasi. Bu yerda faqat NativePHP'ga xos qismni chuqur ochamiz.
1-qism: Ilova menyusi (Application Menu)¶
Ilova menyusi β bu macOS'da ekranning yuqori chetidagi, Windows/Linux'da odatda oyna tepasidagi menyu satri (Fayl, Tahrir, Ko'rish...). Uni Menu facade'i bilan quramiz.
Menu facade: ikki rejim¶
Native\Desktop\Facades\Menu ikki xil ishlaydi:
Menu::create(...)β menyuni quradi va darhol ro'yxatga oladi (ilova menyusiga aylantiradi). Hech narsa qaytarmaydi (void).Menu::make(...)β menyuni quradi, lekin ro'yxatga olmaydi.Native\Desktop\Menu\Menuobyektini qaytaradi. Buni keyinroq kontekst menyu yoki submenu sifatida ishlatamiz.
Eng oddiy ilova menyusi:
use Native\Desktop\Facades\Menu;
Menu::create(
Menu::app(), // macOS: ilova nomi + About, Services, Quit
Menu::file(), // Fayl menyusi
Menu::edit(), // Tahrir: Undo, Redo, Cut, Copy, Paste
Menu::view(), // Ko'rish: Fullscreen, DevTools
Menu::window(), // Oyna: Minimize, Zoom
);
Menu::app(), Menu::file(), Menu::edit() va boshqalar β bular tayyor "role" elementlari. Operatsion tizim ularni avtomatik to'g'ri tarjima qiladi va to'g'ri xulq beradi (masalan Menu::edit() ichidagi "Nusxa olish" haqiqatan tanlangan matnni nusxalaydi). Bularni o'zingiz qaytadan yozishingiz shart emas.
Manba kodida tasdiqlangan to'liq "role" ro'yxati (vendor/nativephp/desktop/src/Facades/Menu.php):
app(), about(), file(), edit(), view(), window(), help(), fullscreen(), separator(), devTools(), undo(), redo(), cut(), copy(), paste(), pasteAndMatchStyle(), reload(), minimize(), close(), quit(), hide().
Har biri ixtiyoriy ?string $label qabul qiladi β masalan Menu::quit('Ilovadan chiqish') bilan yorliqni o'zbekchaga o'zgartirasiz.
Menyu elementlari: label, link, route, separator¶
Tayyor role'lardan tashqari o'z elementlaringizni qo'shasiz:
use Native\Desktop\Facades\Menu;
Menu::create(
Menu::app(),
Menu::make(
Menu::label('Boshqaruv paneli')->event(\App\Events\GoToDashboard::class),
Menu::route('settings', 'Sozlamalar', 'CmdOrCtrl+,'),
Menu::separator(),
Menu::link('https://nativephp.com/docs', 'Hujjatlar')->openInBrowser(),
Menu::separator(),
Menu::quit('Chiqish'),
)->label('Fayl'),
);
Manba koddan tasdiqlangan element-fabrikalar va ularning imzolari:
| Metod | Imzo (verbatim) | Tavsif |
|---|---|---|
Menu::label() |
label(string $label, ?string $hotkey = null) |
Oddiy matnli element; bosilganda event berishi mumkin |
Menu::link() |
link(string $url, ?string $label = null, ?string $hotkey = null) |
URL'ga olib boruvchi element |
Menu::route() |
route(string $url, ?string $label = null, ?string $hotkey = null) |
Laravel route'iga olib boruvchi element |
Menu::checkbox() |
checkbox(string $label, bool $checked = false, ?string $hotkey = null) |
Belgilanadigan/olib tashlanadigan element |
Menu::radio() |
radio(string $label, bool $checked = false, ?string $hotkey = null) |
Bittasini tanlash guruhi |
Menu::separator() |
separator() |
Ajratuvchi chiziq |
linkvsroute:route()ichingizdagi Laravel route nomini oladi va uni joriy oynada ochadi.link()bilan tashqi URL berib,->openInBrowser()qo'shsangiz β element tizim brauzeringizda ochiladi (ilova oynasida emas).openInBrowser()faqatLinkelementida bor (manba:src/Menu/Items/Link.php).
Hotkey (akselerator) qo'shish¶
Menyu elementiga klaviatura yorligi (Electron tilida "akselerator") berishning ikki yo'li bor β ikkalasi ham bir xil ishlaydi:
// 1-usul: uchinchi argument sifatida
Menu::label('Tezkor qidiruv', 'CmdOrCtrl+K');
// 2-usul: zanjir bilan ->hotkey() yoki ->accelerator()
Menu::label('Tezkor qidiruv')->hotkey('CmdOrCtrl+K');
Menu::label('Tezkor qidiruv')->accelerator('CmdOrCtrl+K');
Manba kodida hotkey() aslida accelerator() ning taxallusi (src/Menu/Items/MenuItem.php):
CmdOrCtrl β juda foydali: macOS'da Cmd, Windows/Linux'da Ctrl bo'ladi. Akselerator formati (modifikatorlar): Command/Cmd, Control/Ctrl, CommandOrControl/CmdOrCtrl, Alt, Option, Shift, Super, Meta va klavishlar A-Z, 0-9, F1-F24, Space, Enter, Esc va h.k.
Muhim cheklov (docs'dan): ilova menyusining hotkey'lari faqat ilovangiz oynasi fokusda bo'lganda ishlaydi. Ilova fonda turganda ishlovchi yorliq kerak bo'lsa β bu boshqa narsa:
GlobalShortcut(4-qism).
Submenu (ichma-ich menyu)¶
Har bir element ichiga yana menyu joylashtirish mumkin. ->submenu(...) chaqiriladi:
Menu::label('Eksport')->submenu(
Menu::label('PDF sifatida')->event(\App\Events\ExportPdf::class),
Menu::label('CSV sifatida')->event(\App\Events\ExportCsv::class),
Menu::separator(),
Menu::checkbox('Rasmlarni qo\'shish', true),
);
Yoki butun bir bo'lim quryapsiz va unga sarlavha bermoqchisiz: Menu::make(...)->label('Fayl') β make() bilan menyu yasab, ->label() bilan unga nom berasiz (yuqoridagi "Fayl" misolida shunday qildik).
Menyu bosilganda nima bo'ladi? Event'lar¶
Element bosilganda ikki xil reaksiya bo'lishi mumkin:
Menu::route(...)β avtomatik o'sha route'ga o'tadi, qo'shimcha kod kerak emas.->event(EventClass::class)β siz bergan Laravel event'ini dispatch qiladi. Uni listener yoki Livewire eshitadi.
Bu juda kuchli: menyu hodisasi oddiy Laravel event'iga aylanadi va siz uni odatdagidek qayta ishlaysiz.
Avval event yarating:
// app/Events/OpenSettings.php
namespace App\Events;
use Illuminate\Foundation\Events\Dispatchable;
class OpenSettings
{
use Dispatchable;
}
Menyuda ulang:
Listener yozing (sozlamalar oynasini ochadi):
// app/Listeners/OpenSettingsListener.php
namespace App\Listeners;
use App\Events\OpenSettings;
use Native\Desktop\Facades\Window;
class OpenSettingsListener
{
public function handle(OpenSettings $event): void
{
Window::open('settings')
->title('Sozlamalar')
->width(600)
->height(400)
->route('settings');
}
}
Windowfacade'i vaWindow::open()04-bobda batafsil ochilgan. Bu yerda asosiy g'oya: menyu -> event -> listener -> oyna.
To'liq misol: NativeAppServiceProvider¶
Hammasini birlashtiramiz. Bu kod App\Providers\NativeAppServiceProvider ning boot() ichida turadi:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Native\Desktop\Facades\Menu;
use App\Events\OpenSettings;
class NativeAppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Menu::create(
Menu::app(),
Menu::make(
Menu::route('dashboard', 'Boshqaruv paneli', 'CmdOrCtrl+D'),
Menu::separator(),
Menu::label('Sozlamalar', 'CmdOrCtrl+,')
->event(OpenSettings::class),
Menu::separator(),
Menu::quit('Chiqish'),
)->label('Fayl'),
Menu::edit(),
Menu::view(),
Menu::window(),
Menu::make(
Menu::link('https://nativephp.com/docs', 'Hujjatlar')->openInBrowser(),
Menu::link('https://ioqil.uz', 'Muallif sayti')->openInBrowser(),
)->label('Yordam'),
);
}
}
Bu kodni biz tirik Laravel ilovasi ichida ishga tushirib tekshirdik: Menu::make(...) haqiqatan Native\Desktop\Menu\Menu obyektini qaytaradi, ->toArray() to'g'ri tuzilma beradi, submenu elementlari sanaladi, ->hotkey() akseleratorni o'rnatadi. php -l sintaksis xatosi topmadi.
Illustrativ (bu muhitda ishga tushirilmagan): menyuning ekranda qanday ko'rinishi β yuqoridagi rasmda.
native:devbilan ishga tushirganda ilova menyusi rasmiy muhitda shunday paydo bo'ladi. Bu yerda displey/Electron yo'qligi sababli GUI'ni real ko'rsata olmaymiz.
2-qism: Kontekst menyu (Context Menu)¶
Kontekst menyu β sichqonchaning o'ng tugmasini bosganda chiqadigan menyu. NativePHP'da buni ikki xil yo'l bilan qilish mumkin, va ularni adashtirmaslik muhim.
A yo'li: ContextMenu facade (native, PHP'dan)¶
Native\Desktop\Facades\ContextMenu faqat ikkita metodga ega (manba: src/Facades/ContextMenu.php):
Siz Menu::make(...) bilan menyu yasab, uni ContextMenu::register() ga berasiz. Shundan keyin o'ng-bosish o'sha menyuni native ko'rsatadi:
use Native\Desktop\Facades\ContextMenu;
use Native\Desktop\Facades\Menu;
ContextMenu::register(
Menu::make(
Menu::label('Nusxa olish')->event(\App\Events\CopyClicked::class),
Menu::label('Joylashtirish')->event(\App\Events\PasteClicked::class),
Menu::separator(),
Menu::label('Ochish')->submenu(
Menu::label('Brauzerda'),
Menu::label('Yangi oynada'),
),
),
);
// Keyinchalik kontekst menyuni olib tashlash:
// ContextMenu::remove();
Bu menyu ham xuddi ilova menyusidagi elementlardan tashkil topadi β label, separator, submenu, ->event(...) hammasi ishlaydi. Biz buni tirik ilovada tekshirdik: Menu::make() bilan yasalgan menyuning ->toArray() natijasi to'g'ri submenu strukturasini va element'larning event maydonini beradi.
B yo'li: Native.contextMenu() (JavaScript, frontend'dan)¶
Ba'zan kontekst menyu sahifaning muayyan elementi uchun kerak (masalan jadvaldagi bitta qator). Buni frontend JavaScript'da Native yordamchisi orqali qilasiz (docs'dan):
// Blade/Livewire sahifangizdagi <script> ichida
element.addEventListener('contextmenu', (e) => {
e.preventDefault();
Native.contextMenu([
{
label: 'Tahrirlash',
accelerator: 'e',
click(menuItem, window, event) {
// bu yerda harakat
},
},
{ type: 'separator' },
{ label: 'O\'chirish' },
]);
});
Qaysi birini tanlash? Butun ilova/oyna uchun yagona o'ng-bosish menyusi kerak bo'lsa β
ContextMenu::register()(PHP, sodda). Sahifadagi har xil elementga turlicha menyu kerak bo'lsa βNative.contextMenu()(JS, moslashuvchan). Aniq imzolar uchun rasmiy docs'ning Context Menu sahifasiga qarang.
Kontekst menyu hodisasini qayta ishlash¶
->event(...) bilan bog'langan element bosilganda Laravel event dispatch bo'ladi. Livewire komponentida buni eshitish ayniqsa qulay:
// Livewire komponentida
use Native\Desktop\Facades\ContextMenu;
use Native\Desktop\Facades\Menu;
class Editor extends \Livewire\Component
{
public function mount(): void
{
ContextMenu::register(Menu::make(
Menu::label('Saqlash')->event(\App\Events\SaveDocument::class),
));
}
// App\Events\SaveDocument dispatch bo'lganda chaqiriladigan listener
// odatda alohida Listener klassida bo'ladi.
}
3-qism: MenuBar β tray/menubar ilova (fonda ishlovchi)¶
Bu bobning eng qiziq qismi. MenuBar ilova β bu asosiy oynasi bo'lmagan, faqat tray (Windows) yoki menubar (macOS) ikonkasi orqali yashaydigan ilova. Ikonkaga bosganingizda kichik popover oyna ochiladi. Spotify mini-pleyeri, parol menejerlari, ob-havo widgetlari β hammasi shu modelda.
MenuBar yaratish¶
Native\Desktop\Facades\MenuBar::create() chaqirilganda PendingCreateMenuBar obyekti qaytadi va siz uni fluent (zanjir) usulda sozlaysiz. Eng muhim nozik detal (manba: src/MenuBar/PendingCreateMenuBar.php): obyekt __destruct da avtomatik create() qiladi β ya'ni zanjirni tugatib qo'yib yuborganingizda menubar haqiqatan yaratiladi.
use Native\Desktop\Facades\MenuBar;
use Native\Desktop\Facades\Menu;
MenuBar::create()
->label('Holatim')
->tooltip('Bosib oching')
->icon(storage_path('app/trayIcon.png'))
->route('tray.popover') // popover oyna qaysi route'ni yuklaydi
->width(360)
->height(420)
->withContextMenu(Menu::make(
Menu::label('Mening ilovam'),
Menu::separator(),
Menu::checkbox('Bildirishnomalar', true)
->event(\App\Events\ToggleNotifications::class),
Menu::link('https://nativephp.com', 'Sayt')->openInBrowser(),
Menu::separator(),
Menu::quit('Chiqish'),
));
Muhim (manba'dan):
create()chaqirilganda dock ikonka avtomatik yashiriladi (MenuBar ilova odatda dock'da ko'rinmaydi). Agar dock ikonkasini ham ko'rsatmoqchi bo'lsangiz:->showDockIcon().
MenuBar fluent metodlari (manba'dan tasdiqlangan)¶
src/MenuBar/MenuBar.php dagi zanjirlanadigan metodlar:
| Metod | Tavsif |
|---|---|
->label(string) |
Ikonka yonidagi matn |
->tooltip(string) |
Ikonka ustiga kelganda chiqadigan yozuv |
->icon(string) |
Ikonka fayli yo'li (22x22 PNG tavsiya etiladi) |
->route(string) / ->url(string) |
Popover qaysi sahifani yuklashi |
->width(int) / ->height(int) |
Popover o'lchami |
->resizable(bool) |
O'lcham o'zgartirishga ruxsat |
->withContextMenu(Menu) |
O'ng-bosish kontekst menyusi |
->onlyShowContextMenu(bool) |
Faqat kontekst menyu, popover oynasiz |
->showDockIcon(bool) |
Dock ikonkasini ko'rsatish |
->alwaysOnTop(bool) |
Doim yuqorida (asosan dev paytida qulay) |
->showOnAllWorkspaces(bool) |
Barcha workspace'larda ko'rinish |
->vibrancy(string) |
macOS shaffoflik effekti |
->webPreferences(array) |
Webview sozlamalari |
MenuBar facade'ining o'zidagi to'g'ridan-to'g'ri metodlar (manba: src/Facades/MenuBar.php): create(), show(), hide(), label(), tooltip(), icon(), resize(int $width, int $height), contextMenu(Menu $menu), showContextMenu(), webPreferences(array).
->label()ikki kontekstda:MenuBarfacade'ida ham, fluentPendingCreateMenuBarda hamlabelbor β biri ikonka yonidagi matnni o'rnatadi.
Popover oynasiga UI joylash¶
Popover β bu webview oyna, demak unga oddiy Blade yoki Livewire sahifa qo'yasiz. ->route('tray.popover') bilan qaysi route ekanini bildirdik. Route'ni odatdagidek routes/web.php da e'lon qilasiz:
// routes/web.php
use Illuminate\Support\Facades\Route;
Route::view('/tray', 'tray.popover')->name('tray.popover');
Blade ko'rinishi (resources/views/tray/popover.blade.php):
<div style="padding: 16px; font-family: sans-serif;">
<h3>Bugungi vazifalar</h3>
<ul>
@foreach ($tasks ?? [] as $task)
<li>{{ $task }}</li>
@endforeach
</ul>
<button onclick="alert('Yangi qo\'shildi')">+ Yangi</button>
</div>
Livewire bilan ishlatsangiz, popover ichida real-vaqt holatini ko'rsatishingiz mumkin (masalan tugamagan vazifalar soni). Bu tray ilovalarini ayniqsa kuchli qiladi.
MenuBar hodisalari¶
Tray ikonkasi bilan o'zaro ta'sirni Laravel event'lari orqali tutasiz (manba: src/Events/MenuBar/):
| Event | Qachon |
|---|---|
MenuBarClicked |
Ikonka chap tugma bilan bosilganda |
MenuBarRightClicked |
Ikonka o'ng tugma bilan bosilganda |
MenuBarDoubleClicked |
Ikonka ikki marta bosilganda |
MenuBarShown / MenuBarHidden |
Popover ochilganda / yopilganda |
MenuBarDroppedFiles |
Ikonka ustiga fayl tashlanganda |
MenuBarCreated |
MenuBar yaratilganda |
Masalan MenuBarClicked ning konstruktori (manba'dan): public function __construct(public array $combo, public array $bounds, public array $position). Ya'ni listener'da bosilgan klavisha kombinatsiyasi, ikonka o'lchami va kursor pozitsiyasini olasiz.
MenuBarDroppedFiles esa drag-and-drop'ni ushlaydi: public function __construct(public array $files = []) β tashlangan fayllar ro'yxati keladi.
// app/Listeners/HandleTrayClick.php
namespace App\Listeners;
use Native\Desktop\Events\MenuBar\MenuBarRightClicked;
class HandleTrayClick
{
public function handle(MenuBarRightClicked $event): void
{
logger('Tray o\'ng bosildi', $event->position);
}
}
Illustrativ: tray ikonkasining real ko'rinishi, popover'ning ochilishi va drag-drop β bularning hammasi
native:devbilan tirik Electron muhitida ishlaydi. Bu muhitda displey va Electron yo'q, shuning uchun yuqoridagi kodning sintaksisi va Laravel bilan integratsiyasi tekshirilgan, lekin GUI real ko'rsatilmagan. Soxta "tray paydo bo'ldi" demaymiz.
4-qism: Global yorliqlar (GlobalShortcut)¶
Ilova menyusining hotkey'lari faqat oyna fokusda bo'lsa ishlaydi. Lekin ba'zan ilova fonda turganda ham yorliq kerak β masalan ekrandagi istalgan joyda Ctrl+Shift+Space bosib tezkor qidiruvni chaqirish (Spotlight, Raycast, Alfred kabi). Bu β global yorliq.
GlobalShortcut facade¶
Native\Desktop\Facades\GlobalShortcut to'rt metodga ega (manba: src/Facades/GlobalShortcut.php):
// @method static \Native\Desktop\GlobalShortcut key(string $key)
// @method static \Native\Desktop\GlobalShortcut event(string $event)
// @method static void register()
// @method static void unregister()
Foydalanish β zanjir bilan: key() -> event() -> register():
use Native\Desktop\Facades\GlobalShortcut;
use App\Events\QuickSearchPressed;
GlobalShortcut::key('CmdOrCtrl+Shift+Space')
->event(QuickSearchPressed::class)
->register();
Bekor qilish:
Biz buni tirik ilovada GlobalShortcut::fake() bilan tekshirdik: key()->event()->register() zanjiri va key()->unregister() to'g'ri ishlaydi, xato bermaydi.
Event'ni qayta ishlash¶
QuickSearchPressed oddiy Laravel event'i:
// app/Events/QuickSearchPressed.php
namespace App\Events;
use Illuminate\Foundation\Events\Dispatchable;
class QuickSearchPressed
{
use Dispatchable;
public function __construct(public ?string $combo = null)
{
}
}
Listener uni eshitib, masalan tezkor qidiruv oynasini ochadi:
// app/Listeners/ShowQuickSearch.php
namespace App\Listeners;
use App\Events\QuickSearchPressed;
use Native\Desktop\Facades\Window;
class ShowQuickSearch
{
public function handle(QuickSearchPressed $event): void
{
Window::open('quick-search')
->width(640)
->height(80)
->alwaysOnTop()
->route('quick-search');
}
}
Akselerator formati va eng yaxshi amaliyotlar¶
Global yorliq stringi xuddi menyu hotkey'i kabi formatda: modifikator(lar) + klavisha. Masalan CmdOrCtrl+Shift+P, Alt+F4, Super+L.
Maslahatlar:
- Ro'yxatga olishni
boot()da bir marta qiling β ilova ishga tushganda. Bu yorliqni ilova fonda turganda ham faollashtiradi. - Tizim yorliqlarini "o'g'irlamang".
CmdOrCtrl+C,Cmd+Tab,Alt+F4kabilarni global qilib qo'ymang β foydalanuvchini g'azablantirasiz va OS ruxsat bermasligi mumkin. - Noyob kombinatsiya tanlang. Kamida ikkita modifikator (
CmdOrCtrl+Shift+...) konfliktni kamaytiradi. - Tozalash. Ilova yopilganda OS global yorliqlarni avtomatik bo'shatadi, lekin yorliqni dinamik o'zgartirsangiz
->unregister()ni unutmang.
Illustrativ: global yorliqning real bosilishi faqat tirik Electron muhitida (
native:dev/native:build) ishlaydi β ElectronglobalShortcutAPI'sini OS darajasida ro'yxatga oladi. Bu muhitda bu jarayon ishga tushirilmagan; biz PHP tomonidagi builder va event/listener mantig'ini tekshirdik, xolos.
Hammasi birga: amaliy mini-arxitektura¶
Tasavvur qiling, "Vazifalar" nomli tray ilovasi yaratyapsiz:
- MenuBar ilova β tray ikonkasi, bosilganda popover'da bugungi vazifalar ro'yxati (Livewire).
- Kontekst menyu β ikonkaga o'ng-bosish: "Yangi vazifa", "Hammasini bajarildi deb belgilash", "Chiqish".
- Global yorliq β
CmdOrCtrl+Shift+Tistalgan joyda yangi vazifa qo'shish oynasini ochadi. - Ilova menyusi β agar to'liq oyna ham bo'lsa, yuqorida standart Fayl/Tahrir menyusi.
Bularning barchasi NativeAppServiceProvider::boot() da yig'iladi, har bir hodisa Laravel event'iga aylanadi, va siz ularni odatdagi listener/Livewire eshituvchilari bilan qayta ishlaysiz. NativePHP'ning go'zalligi shunda β native qobiq murakkabligi sizdan yashiringan, siz esa odatdagi Laravel kodi yozasiz.
Ma'lumotlar bazasi bilan ishlash (vazifalarni saqlash) β bu sof Laravel/Eloquent. Esga olish uchun: SQL qo'llanmasi, Laravel.
Mashqlar¶
Oson¶
-
NativeAppServiceProvider::boot()ichidaMenu::create(...)bilan minimal ilova menyusi yozing:Menu::app(),Menu::file(),Menu::edit(). Faqat shu uchta role. -
"Fayl" menyusiga
Menu::quit('Ilovadan chiqish')elementiniCmdOrCtrl+Qhotkey bilan qo'shing. Hotkey'ni ikki xil usulda (argument va->hotkey()) yozib ko'rsating. -
Menu::link('https://ioqil.uz', 'Muallif')elementini yarating va uni tizim brauzerida ochiladigan qiling. Qaysi metod buni ta'minlaydi? -
MenuBar::create()bilan eng oddiy tray ilovasi yozing: faqat->label('Salom')va->tooltip('Bosing'). Boshqa hech narsa. -
GlobalShortcut::key('CmdOrCtrl+Shift+H')->unregister();qatorini tushuntiring β bu nima qiladi va qachon kerak bo'ladi? -
Menu::checkbox('Qorong\'u rejim', true)vaMenu::radio('Kichik shrift')orasidagi farqni bir-ikki jumlada yozing.
O'rta¶
-
To'liq
NativeAppServiceProvider::boot()yozing: ilova menyusida "Fayl" bo'limiMenu::make(...)->label('Fayl')orqali qurilsin, ichidaMenu::route('dashboard', 'Bosh sahifa', 'CmdOrCtrl+D')vaMenu::separator()vaMenu::quit()bo'lsin. -
Tray ilovasi yarating:
->route('tray.popover'),->width(320)->height(400), va->withContextMenu(...)ichidaMenu::label,Menu::separator,Menu::quit. Mosroutes/web.phpqatorini ham yozing. -
Menu::label('Sozlamalar')->event(\App\Events\OpenSettings::class)uchun to'liq zanjir yozing: event klassi (Dispatchabletrait bilan) + listener klassi (Window::open(...)chaqiruvi bilan). -
Global yorliq
CmdOrCtrl+Shift+SpaceniQuickSearchPressedevent'iga ulang. Event klassini va uniboot()da ro'yxatga olishni yozing. -
Menu::label('Eksport')->submenu(...)bilan ichma-ich menyu yarating: submenu ichida "PDF" va "CSV" elementlari, har biri o'z event'iga ulangan. -
ContextMenu::register(Menu::make(...))bilan global kontekst menyu yarating: "Nusxa olish", ajratuvchi, va "Ochish" submenu'si (ichida ikkita element). Keyin uni qanday olib tashlashni (ContextMenu::remove()) izohlang.
Qiyin¶
-
Arxitektura savoli: "Vazifalar" tray ilovasini loyihalashtiring. Quyidagilarni qaysi facade/event bilan qilishni tushuntiring (kod skeletini yozing): (a) tray popover'da Livewire ro'yxati, (b) o'ng-bosish kontekst menyu "Yangi vazifa", (c) global
CmdOrCtrl+Shift+Tbilan tezkor qo'shish oynasi. Har bir bo'lakniboot()da qanday yig'ishni ko'rsating. -
Ikki tomonlama farq: Kontekst menyuni
ContextMenu::register()(PHP) vaNative.contextMenu()(JS) bilan qilish o'rtasidagi farqni tahlil qiling. Qaysi holatda qaysi birini tanlaysiz? Jadval/qatorlar bilan ishlovchi ilova uchun nima tavsiya qilasiz va nega? Har biriga qisqa kod misol bering. -
Hodisa konstruktorlari:
MenuBarClickedvaMenuBarDroppedFilesevent'larining konstruktorlari qanday (qaysi public xususiyatlar)? Listener'da fayl drag-drop ni ushlaydigan to'liq listener yozing va tashlangan fayllarni log qiling. -
Hotkey doirasi: Ilova menyusi hotkey'i (
Menu::label('X', 'CmdOrCtrl+K')) va global yorliq (GlobalShortcut::key('CmdOrCtrl+K')) o'rtasidagi tub farqni tushuntiring β qaysi biri ilova fonda turganda ishlaydi, qaysi biri faqat oyna fokusda? Bir xil kombinatsiyani ikkalasiga ham bersangiz qanday muammo chiqishi mumkin?
Yechimlar
Oson¶
1.
Menu::app() faqat macOS'da ko'rinadi (ilova nomi menyusi); Windows/Linux'da e'tiborga olinmaydi β bu normal.
2.
// 1-usul: argument
Menu::quit('Ilovadan chiqish')->accelerator('CmdOrCtrl+Q');
// quit() label argument oladi, akseleratorni esa zanjir bilan beramiz
// label uchun 3-argument usuli:
Menu::label('Chiqish', 'CmdOrCtrl+Q'); // argument
Menu::label('Chiqish')->hotkey('CmdOrCtrl+Q'); // zanjir
hotkey() β bu accelerator() taxallusi, ikkalasi bir xil.
3.
->openInBrowser() metodi (faqat Link elementida bor) elementni ilova oynasida emas, tizim brauzerida ochadi.
4.
PendingCreateMenuBar obyekti __destruct da avtomatik create() qiladi, shuning uchun qo'shimcha chaqiruv kerak emas.
5. GlobalShortcut::key('CmdOrCtrl+Shift+H')->unregister(); β oldin ro'yxatga olingan global yorliqni o'chiradi (OS endi bu kombinatsiyani ilovaga yubormaydi). Kerak bo'ladi: foydalanuvchi sozlamalarda yorliqni o'chirsa, yoki yorliqni boshqasiga almashtirayotganda (avval eskisini unregister, keyin yangisini register).
6. checkbox β mustaqil yoqilgan/o'chirilgan holat (bir nechtasi bir vaqtda belgilangan bo'lishi mumkin, masalan "Bildirishnomalar", "Avtomatik saqlash"). radio β guruh ichidan faqat bittasi tanlanadi (masalan shrift o'lchami: Kichik/O'rta/Katta).
O'rta¶
7.
public function boot(): void
{
Menu::create(
Menu::app(),
Menu::make(
Menu::route('dashboard', 'Bosh sahifa', 'CmdOrCtrl+D'),
Menu::separator(),
Menu::quit('Chiqish'),
)->label('Fayl'),
Menu::edit(),
Menu::view(),
Menu::window(),
);
}
8.
// NativeAppServiceProvider::boot()
MenuBar::create()
->route('tray.popover')
->width(320)
->height(400)
->withContextMenu(Menu::make(
Menu::label('Vazifalar ilovasi'),
Menu::separator(),
Menu::quit('Chiqish'),
));
9.
// app/Events/OpenSettings.php
namespace App\Events;
use Illuminate\Foundation\Events\Dispatchable;
class OpenSettings { use Dispatchable; }
// app/Listeners/OpenSettingsListener.php
namespace App\Listeners;
use App\Events\OpenSettings;
use Native\Desktop\Facades\Window;
class OpenSettingsListener
{
public function handle(OpenSettings $event): void
{
Window::open('settings')->title('Sozlamalar')->width(600)->height(400)->route('settings');
}
}
EventServiceProvider da qo'lda ulaysiz).
10.
// app/Events/QuickSearchPressed.php
namespace App\Events;
use Illuminate\Foundation\Events\Dispatchable;
class QuickSearchPressed { use Dispatchable; public function __construct(public ?string $combo = null) {} }
// NativeAppServiceProvider::boot()
GlobalShortcut::key('CmdOrCtrl+Shift+Space')
->event(\App\Events\QuickSearchPressed::class)
->register();
11.
Menu::label('Eksport')->submenu(
Menu::label('PDF sifatida')->event(\App\Events\ExportPdf::class),
Menu::label('CSV sifatida')->event(\App\Events\ExportCsv::class),
);
->submenu(...) ichidagi elementlar Menu::make() orqali yangi Menu obyektiga yig'iladi (manba: MenuItem::submenu()).
12.
ContextMenu::register(Menu::make(
Menu::label('Nusxa olish')->event(\App\Events\CopyClicked::class),
Menu::separator(),
Menu::label('Ochish')->submenu(
Menu::label('Brauzerda'),
Menu::label('Yangi oynada'),
),
));
ContextMenu::remove(); β endi o'ng-bosish hech qanday native menyu ko'rsatmaydi (default brauzer menyusiga qaytadi yoki hech narsa). register/remove β ContextMenu facade'idagi yagona ikkita metod.
Qiyin¶
13. Mini-arxitektura skeleti:
// NativeAppServiceProvider::boot()
public function boot(): void
{
// (a) + (b): tray popover + kontekst menyu
MenuBar::create()
->label('Vazifalar')
->icon(storage_path('app/trayIcon.png'))
->route('tray.popover') // Livewire komponentli sahifa
->width(360)->height(440)
->withContextMenu(Menu::make(
Menu::label('Yangi vazifa')->event(\App\Events\NewTaskRequested::class),
Menu::label('Hammasini bajarildi')->event(\App\Events\MarkAllDone::class),
Menu::separator(),
Menu::quit('Chiqish'),
));
// (c): global yorliq
GlobalShortcut::key('CmdOrCtrl+Shift+T')
->event(\App\Events\NewTaskRequested::class)
->register();
}
{{-- resources/views/tray/popover.blade.php --}}
@livewire('task-list') {{-- Livewire komponenti real-vaqt ro'yxat ko'rsatadi --}}
// Listener: NewTaskRequested -> qo'shish oynasini ochadi
class OpenNewTaskWindow {
public function handle(\App\Events\NewTaskRequested $e): void {
Window::open('new-task')->width(480)->height(200)->alwaysOnTop()->route('tasks.new');
}
}
NewTaskRequested event'ini dispatch qiladi β kod takrorlanmaydi, ikkala kirish nuqtasi bitta listener'ga olib boradi. Vazifalarni saqlash sof Eloquent.
14. Farq:
- ContextMenu::register() (PHP): butun oyna/ilova uchun bitta global o'ng-bosish menyusi. Menyu native, PHP'da tasvirlanadi, elementlar Laravel event'lari bilan ulanadi. Sodda, server tomonida boshqariladi.
- Native.contextMenu() (JS): frontend'da, muayyan DOM elementi uchun. Har bir element/qatorga turlicha menyu bera olasiz, click callback'i bevosita JS'da.
Tavsiya: jadval/qatorlar bilan ishlovchi ilovada (har qatorga "Tahrirlash/O'chirish" kerak) β Native.contextMenu() ni element contextmenu hodisasida ishlatib, qator ID'sini callback'da yuborish qulayroq. Yagona umumiy menyu (masalan "Yopishtirish/Bekor qilish") bo'lsa β ContextMenu::register() PHP'da soddaroq.
// PHP global
ContextMenu::register(Menu::make(Menu::label('Yopishtirish')->event(PasteEvent::class)));
// JS, qatorga xos
row.addEventListener('contextmenu', e => {
e.preventDefault();
Native.contextMenu([{ label: 'O\'chirish', click: () => deleteRow(row.dataset.id) }]);
});
15. Manba kodidan:
- MenuBarClicked: public function __construct(public array $combo, public array $bounds, public array $position)
- MenuBarDroppedFiles: public function __construct(public array $files = [])
Drag-drop listener:
namespace App\Listeners;
use Native\Desktop\Events\MenuBar\MenuBarDroppedFiles;
class HandleDroppedFiles
{
public function handle(MenuBarDroppedFiles $event): void
{
foreach ($event->files as $path) {
logger('Tray ustiga fayl tashlandi: ' . $path);
}
}
}
16. Tub farq:
- Menyu hotkey (Menu::label('X', 'CmdOrCtrl+K')) β bu ilova menyusiga bog'langan akselerator. Docs aniq aytadi: u faqat ilovangiz oynasi fokusda bo'lganda (yoki tegishli kontekst menyu ochiq bo'lganda) ishlaydi. Ilova fonda bo'lsa β ishlamaydi.
- GlobalShortcut (GlobalShortcut::key('CmdOrCtrl+K')->register()) β bu OS darajasida ro'yxatga olinadi va ilova fonda bo'lganda ham, boshqa ilova fokusda bo'lganda ham ishlaydi.
Bir xil kombinatsiyani ikkalasiga bersangiz: ilova fokusda bo'lganda konflikt yuzaga keladi (qaysi biri birinchi ushlaydi β aniq emas, OS/Electron xulqiga bog'liq), va global yorliq menyu hotkey'ini "ustun bosib" qo'yishi mumkin. Eng yaxshi amaliyot: global yorliqlar uchun boshqacha, noyobroq kombinatsiya tanlash (masalan menyu uchun CmdOrCtrl+K, global uchun CmdOrCtrl+Shift+Space).
β¬ οΈ Oldingi: 04 β Oyna boshqaruvi (Window) Β· π README Β· Keyingi: 06 β Native UI: Notification, Dialog, Alert β‘οΈ