04 β Oyna boshqaruvi (Window)¶
β¬ οΈ Oldingi: 03 β Birinchi desktop ilova Β· π README Β· Keyingi: 05 β Menyu, tray va global yorliqlar β‘οΈ
Bu bobda: NativePHP desktop ilovasining eng asosiy obyekti β oynani (window) to'liq boshqarishni o'rganamiz.
Windowfacade orqali oyna ochish (open), uning sarlavhasi (title), o'lchami (width/height), minimal va maksimal o'lchamlari (minWidth/maxWidth...), ekrandagi joyi (position), bir nechta oynani unikalidbilan boshqarish, oynani yopish/qayta yuklash/kichraytirish/kattalashtirish (close/reload/minimize/maximize), o'lchamga oid xulq (resizable,movable,minimizable,maximizable,closable), ramkasiz (frameless) va shaffof (transparent) oyna, sarlavha panelini yashirish (titleBarHidden), to'liq ekran (fullscreen), doim ustda (alwaysOnTop), holatni eslab qolish (rememberState) va oynalararo ma'lumot almashish yo'llarini ko'rib chiqamiz. Oxirida oyna hayot-tsikli hodisalari (WindowShown,WindowClosed, ...) va dev rejimdagi avtomatik qayta yuklashga to'xtalamiz.Halol eslatma: NativePHP "sof native widget" yaratmaydi. Har bir oyna β bu Electron
BrowserWindow(native ramka), uning ichida esa sizning Laravel UI'ngiz (Blade/Livewire/Inertia) webview (Chromium) ichida ishlaydi.Window::open(...)chaqirig'i PHP'dan ichki HTTP API orqali Electron'ga buyruq yuboradi. Shu sababli bu bobdagi PHP/Laravel kodi haqiqiy tekshirilgan (controller, route,Window::fake()bilan feature test vatoArray()konfiguratsiyasi ishlaydi), lekin haqiqiy oyna ochilishi, GUI ko'rinishi, hot-reload,native:dev/native:buildβ bularning hammasi displey + Electron + Node muhitini talab qiladi va bu yerda illustrativ (kod/buyruq to'g'ri, lekin shu hujjat muhitida ishga tushirilmagan) deb belgilanadi.
Oyna nima: webview ramkasi¶
Brauzerda sahifa ochilganda u brauzer oynasi ichida bo'ladi. NativePHP'da xuddi shunday: sizning Laravel sahifangiz (route) Electron BrowserWindow degan native oyna ichidagi Chromium webview'da render bo'ladi. Ya'ni oyna β bu "ramka + ichidagi web sahifa".
Bu juda muhim aqliy model:
- Native qism β oynaning ramkasi, sarlavha paneli, kichraytirish/kattalashtirish/yopish tugmalari, ekrandagi joyi, o'lchami. Bularni
Windowfacade boshqaradi. - Web qism β oyna ichidagi kontent. Bu sizning oddiy Laravel route'ingiz (
route('settings.index')), HTML/CSS/JS bilan. Bu yerda hech qanday "native" sehr yo'q β bu webview. - PHP qism β
Window::open(...)kabi buyruqlar bundle qilingan PHP binar ichida ishlaydi va ichki HTTP API orqali Electron'ga "oyna och" deb aytadi.
Agar Laravel asoslarini eslatish kerak bo'lsa: route, controller va Blade bo'yicha ../laravel/README.md ga qarang. Bu bobda Laravel'ni qayta o'rgatmaymiz β faqat NativePHP'ga xos qismni chuqurlashtiramiz.
Window facade va uning namespace'i¶
NativePHP desktop (nativephp/desktop) paketida oyna boshqaruvi Window facade orqali amalga oshiriladi. To'g'ri namespace:
Diqqat (versiya farqi): Eski materiallarda
Native\Laravel\Facades\Windowko'rishingiz mumkin. Joriynativephp/desktop(v2) paketida facadeNative\Desktop\Facades\Window. Aniq namespace uchun o'zvendor/nativephp/desktop/src/Facades/Window.phpfaylingizga yoki rasmiy hujjatga qarang.
Bu kitobdagi barcha kod Native\Desktop\Facades\Window ni ishlatadi va u haqiqatda o'rnatilgan paketdan tekshirilgan.
Oyna ochish: Window::open()¶
Eng oddiy chaqiruv:
open() ning imzosi: open(string $id = 'main'). Agar id bermasangiz, oynaning ID'si 'main' bo'ladi. NativePHP ilova ishga tushganda odatda main oynasini avtomatik ochadi (buni ServiceProvider da sozlaysiz β keyingi bo'limda).
open() zanjirlanadigan (chainable) sozlash metodlari bilan birga ishlatiladi. Masalan:
Window::open('settings')
->title('Sozlamalar')
->route('settings.index')
->width(640)
->height(480);
Bu yerda:
open('settings')βsettingsID'li yangi oyna.title('Sozlamalar')β sarlavha paneliga matn.route('settings.index')β oyna ichida ko'rsatiladigan Laravel route (nomli route).width(640),height(480)β oyna o'lchami (piksel).
Texnik nozik nuqta:
Window::open(...)aslidaPendingOpenWindowobyektini qaytaradi. Siz unga zanjir bilan sozlamalarni qo'shasiz, va ushbu obyekt PHP tomonidan yo'q qilinganda (destruct) to'plangan konfiguratsiya ichki API'ga yuboriladi β shunda Electron oynani yaratadi. Amalda bu sizga ko'rinmaydi: shunchaki zanjirni yozasiz va oyna ochiladi.
Kontent: route() va url()¶
Oyna ichida nima ko'rsatilishini ikki yo'l bilan beramiz:
// 1) Nomli Laravel route β eng ko'p ishlatiladigan usul
Window::open('settings')->route('settings.index');
// 2) Parametr bilan route
Window::open('profile')->route('users.show', ['id' => 5]);
// 3) To'g'ridan-to'g'ri URL (ichki yoki tashqi)
Window::open('docs')->url('https://nativephp.com');
Ichki manbada route() shunchaki Laravel'ning route() helper'ini chaqirib, natijani url() ga uzatadi:
// HasUrl trait ichidan (soddalashtirilgan)
public function route(string $route, array $parameters = []): self
{
$this->url(route($route, $parameters));
return $this;
}
Demak route('users.show', ['id' => 5]) β bu oddiy Laravel nomli route va parametr. Bu oynalararo ma'lumot uzatishning eng oddiy yo'li ham (bu haqda pastda).
O'lcham: width, height va min/max¶
Window::open('editor')
->width(1024)
->height(720)
->minWidth(600) // bundan kichik qila olmaydi
->minHeight(400)
->maxWidth(1600) // bundan katta qila olmaydi
->maxHeight(1000);
Standart qiymatlar (paket manbasidan): width = 400, height = 400, minWidth/minHeight/maxWidth/maxHeight = 0 (ya'ni cheklov yo'q). 0 qiymati "cheklanmagan" degani.
Oyna ochilgandan keyin ham o'lchamni o'zgartirish mumkin β buning uchun resize ishlatiladi:
Joy (position) va ekran¶
Oynani ekranning aniq nuqtasiga qo'yish:
// open() zanjirida: position($x, $y)
Window::open('hud')
->width(300)
->height(150)
->position(40, 40); // chap-yuqori burchakdan 40px
// ochilgan oynani keyin ko'chirish: position($x, $y, $animated = false, $id = null)
Window::position(100, 100, true, 'hud'); // animatsiya bilan
Diqqat:
open()zanjiridagiposition($x, $y)(ikki argument) β bu boshlang'ich joy. Ochilgan oynani keyin ko'chirish uchun esa facade'dagiWindow::position($x, $y, $animated, $id)(to'rt argument) ishlatiladi. Ular farqli β birinchisi konfiguratsiya, ikkinchisi runtime buyruq.
Bir nechta oyna va ularni ID bilan boshqarish¶
NativePHP'da har bir oyna unikal id bilan tanib olinadi. ID β bu oddiy satr. Bir nechta oyna ochib, har birini ID orqali alohida boshqarasiz.
use Native\Desktop\Facades\Window;
// Asosiy oyna
Window::open(); // id = 'main'
// Sozlamalar oynasi
Window::open('settings')
->title('Sozlamalar')
->route('settings.index')
->width(640)->height(480)
->minWidth(400)->minHeight(300);
// Dastur haqida oynasi β kichik, o'lchami o'zgarmas
Window::open('about')
->title('Dastur haqida')
->route('about')
->width(420)->height(320)
->resizable(false)
->maximizable(false);
Keyin har bir oynani ID bo'yicha boshqarasiz:
Window::close('settings'); // settings oynasini yopish
Window::minimize('about'); // about oynasini kichraytirish
Window::maximize('main'); // main oynasini kattalashtirish
Window::reload('main'); // main oynasini qayta yuklash
Window::alwaysOnTop(true, 'about'); // about doim ustda
id bermasangiz (null), odatda joriy/asosiy oynaga ta'sir qiladi:
Amaliy maslahat: ID'larni bir joyda konstanta sifatida saqlang (masalan
enumyokiclass const). "settings" deb har joyda qo'lda yozish o'rnigaWindowId::Settings->valueishlatish xatolarni kamaytiradi.
Joriy va barcha oynalarni olish¶
// current() Native\Desktop\Windows\Window obyektini qaytaradi (stdClass emas).
// Xossalarni oddiy property kabi o'qiysiz ($current->id, $current->width, ...);
// ular ichki __get + fromRuntimeWindow orqali runtime qiymatlardan to'ldiriladi.
$current = Window::current();
$id = $current->id; // 'main', 'settings', ...
$w = $current->width; // joriy kenglik (piksel)
// Barcha ochiq oynalar massivi β har biri ham Window obyekti
$all = Window::all();
foreach ($all as $win) {
// $win->id, $win->title, $win->width, ...
}
Nozik nuqta:
current()vaall()qaytaradigan obyektlarNative\Desktop\Windows\Windowsinfiga tegishli. Bu sinfda__getsehri bor β shuning uchun$current->id,$current->width,$current->alwaysOnTopkabi xossalarni to'g'ridan-to'g'ri o'qiysiz (mavjud bo'lmagan xossanullqaytaradi).
Modal oyna haqida halol gap¶
Ko'p UI freymvorklarida "modal" oyna degan tushuncha bor β ota oynani bloklab, faqat o'ziga javob talab qiladigan oyna. NativePHP desktop hujjatida Window facade'ida maxsus modal() metodi yo'q (joriy v2 da). Shu sababli uni ixtiro qilmaymiz.
Modal his-tuyg'usini quyidagilar bilan taqlid qilish mumkin:
// "Modal kabi" oyna: doim ustda, kichik, qayta o'lchanmaydigan
Window::open('confirm')
->title('Tasdiqlang')
->route('confirm')
->width(360)->height(200)
->resizable(false)
->minimizable(false)
->maximizable(false)
->alwaysOnTop();
Bu haqiqiy OS-darajadagi modal emas (ota oynani to'liq bloklamaydi), lekin foydalanuvchi nuqtai nazaridan o'xshash tajriba beradi. Agar sizga "haqiqiy" modal kerak bo'lsa β ko'pincha eng yaxshi yechim umuman alohida oyna ochmaslik, balki bir oyna ichida Livewire/JS modal (overlay) ko'rsatishdir. Webview'da bu oddiy frontend ishi.
Aniq imzo va yangi imkoniyatlar uchun har doim rasmiy hujjatni tekshiring: nativephp.com/docs/desktop/2/the-basics/windows.
Ramkasiz (frameless) va shaffof (transparent) oyna¶
Standart oynada native sarlavha paneli (title bar) va ramka bo'ladi. Ba'zan zamonaviy ko'rinish uchun ularni olib tashlamoqchi bo'lasiz.
Sarlavha panelini yashirish β titleBarHidden()¶
Window::open('player')
->title('Pleyer')
->width(380)->height(560)
->titleBarHidden(); // sarlavha paneli ko'rinmaydi (titleBarStyle = 'hidden')
titleBarHidden() β bu native sarlavha panelini yashiradi, lekin oyna ramkasini saqlab qoladi. macOS uchun yana titleBarHiddenInset() va titleBarButtonsOnHover() variantlari bor.
To'liq ramkasiz β frameless()¶
Window::open('widget')
->width(300)->height(200)
->frameless(); // umuman ramka yo'q (frame = false)
Ramkasiz oynada native "ushlab ko'chirish" zonasi yo'qoladi. Shuning uchun HTML tarafda ko'chirish zonasini CSS bilan belgilash kerak:
<!-- Oynaning yuqori paneli β bu yerdan ushlab ko'chirish mumkin bo'ladi -->
<div style="-webkit-app-region: drag; height: 40px; background: #2563eb;">
<span style="color:#fff; padding:8px;">Mening widgetim</span>
</div>
<!-- Tugmalar ko'chirilmasligi kerak β ularda no-drag -->
<button style="-webkit-app-region: no-drag;">Yopish</button>
-webkit-app-region: dragβ bu Chromium/Electron'ga "shu element oynani ushlab ko'chirish zonasi" deb aytadi.no-dragesa tugma/inputlarni undan istisno qiladi (aks holda ularni bosib bo'lmaydi).
Shaffof oyna β transparent()¶
Window::open('overlay')
->width(400)->height(300)
->frameless()
->transparent(); // fon shaffof bo'ladi (backgroundColor = '#00000000')
transparent() chaqirilganda paket avtomatik backgroundColor ni #00000000 (to'liq shaffof) qilib qo'yadi. Shaffof oynada faqat HTML kontentingiz ko'rinadi β fon yo'q. Bu suzuvchi vidjet, HUD yoki maxsus shaklli oynalar uchun ishlatiladi.
Yana bir tayyor kombinatsiya bor β invisibleFrameless():
// frameless() + transparent() + focusable(false) + hasShadow(false)
Window::open('hud')->invisibleFrameless();
Bu "ko'rinmas" suzuvchi qatlam yaratadi: ramkasiz, shaffof, fokus olmaydigan, soyasiz. Ekran ustidagi overlay'lar uchun qulay.
Fon rangi β backgroundColor()¶
Maslahat: Oyna ochilganda bir lahza oq fon "yonib" ketishi mumkin (webview yuklanguncha). To'q mavzuli ilovada
backgroundColor('#1e293b')kabi qo'yib, bu noxush miltillashni yo'qotasiz.
Oyna xulqi: resizable, movable va boshqalar¶
Bu metodlar oynaning foydalanuvchi tomonidan boshqarilishini cheklaydi. Hammasi bool qabul qiladi (standart true):
Window::open('locked')
->width(500)->height(400)
->resizable(false) // o'lchamini o'zgartira olmaydi
->movable(false) // ko'chira olmaydi
->minimizable(false) // kichraytira olmaydi
->maximizable(false) // kattalashtira olmaydi
->closable(true) // yopa oladi (standart true)
->focusable(true) // fokus olishi mumkin
->fullscreenable(false); // to'liq ekranga o'tkaza olmaydi
Qisqacha jadval (paket manbasidan tasdiqlangan standart qiymatlar):
| Metod | Standart | Vazifasi |
|---|---|---|
resizable(bool) |
true |
O'lchamini o'zgartirish mumkinmi |
movable(bool) |
true |
Ko'chirish mumkinmi |
minimizable(bool) |
true |
Kichraytirish mumkinmi |
maximizable(bool) |
true |
Kattalashtirish mumkinmi |
closable(bool) |
true |
Yopish mumkinmi |
focusable(bool) |
true |
Fokus olishi mumkinmi |
fullscreenable(bool) |
true |
To'liq ekranga o'tishi mumkinmi |
hasShadow(bool) |
true |
Oyna soyasi bor-yo'qligi |
To'liq ekran, doim ustda va boshqa holatlar¶
// To'liq ekranda ochish
Window::open('present')->fullscreen();
// Kichraytirilgan holda ochish
Window::open('bg')->minimized();
// Kattalashtirilgan holda ochish
Window::open('main')->maximized();
// Doim boshqa oynalar ustida
Window::open('timer')->width(220)->height(120)->alwaysOnTop();
// Kiosk rejimi (to'liq ekran, chiqib bo'lmaydigan β ko'rgazma/terminal uchun)
Window::open('kiosk')->kiosk();
minimized() va maximized() ichki jihatdan "oyna ochilgandan keyin" Window::minimize/Window::maximize ni chaqiradi β ya'ni bular afterOpen orqali ishlaydi.
Doim ustda holatini keyin ham almashtirish mumkin:
Holatni eslab qolish β rememberState()¶
Foydalanuvchi oynani ko'chirib, o'lchamini o'zgartirsa, keyingi safar ilova ochilganda o'sha holat tiklanishini istasangiz:
Window::open('main')
->width(1000)->height(700)
->rememberState(); // joy va o'lcham seanslar orasida saqlanadi
Cheklov (hujjatdan): bir vaqtning o'zida faqat bitta oynaning holati eslab qolinadi. Odatda buni asosiy (
main) oynaga qo'yasiz.
Zum darajasi va DevTools¶
// Kontentni 125% kattalashtirib ochish
Window::open('reader')->zoomFactor(1.25);
// Ochilgan oynada zumni o'zgartirish
Window::zoomFactor(1.5);
Dasturchi vositalari (DevTools β webview'ni tekshirish uchun):
Standart holatda
showDevToolsqiymaticonfig('app.debug')ga teng β ya'niAPP_DEBUG=truebo'lsa, dev rejimda DevTools o'z-o'zidan mavjud bo'ladi. Ishlab chiqarish (production) build'ida bunifalseqilib qo'ying.
Menyu va navigatsiyani cheklash¶
Window::open('app')
->hideMenu() // native menyu panelini avto-yashirish
->preventLeaveDomain() // boshqa domenga o'tishni bloklash (xavfsizlik)
->preventLeavePage() // joriy sahifadan chiqishni bloklash
->suppressNewWindows(); // yangi oyna ochilishini bostirish (target=_blank)
preventLeaveDomain() ayniqsa muhim: agar oynangizda tashqi havolalar bo'lsa, foydalanuvchi tasodifan ilovangizdan "chiqib", oddiy brauzerga aylanib qolmasligi uchun shuni yoqing.
Webview sozlamalari β webPreferences()¶
Quyi darajadagi Electron sozlamalari kerak bo'lsa:
Window::open('advanced')->webPreferences([
'spellcheck' => true,
// ... boshqa Electron BrowserWindow webPreferences kalitlari
]);
Bu yerda berilgan massiv to'g'ridan-to'g'ri Electron'ning webPreferences ob'ektiga uzatiladi. Aniq kalitlar uchun Electron hujjatiga qarang β bu NativePHP'ning "qochish lyuki" (escape hatch).
Oynalararo ma'lumot almashish¶
Bir nechta oyna bir xil PHP jarayonini va bir xil Laravel ilovasini bo'lishadi. Demak ular orasida ma'lumot uzatishning bir nechta tabiiy yo'li bor:
1. Umumiy ma'lumotlar bazasi / cache (eng sodda). Barcha oynalar bir xil SQLite/DB va Cache:: ga kirishadi. Bir oynada saqlang, boshqasida o'qing:
// settings oynasida
\Illuminate\Support\Facades\Cache::put('theme', 'dark');
// main oynasida (qayta yuklangandan keyin)
$theme = \Illuminate\Support\Facades\Cache::get('theme', 'light');
2. Route parametrlari (ochishda ma'lumot uzatish). Oynani ochayotganda kontekstni route'ga beramiz:
3. Broadcast hodisalar (real vaqtli sinxron). NativePHP oyna hodisalarini nativephp kanaliga ShouldBroadcastNow bilan broadcast qiladi. Siz ham o'z hodisalaringizni shu kanalga yuborib, barcha oynalardagi frontendni (Laravel Echo bilan) yangilashingiz mumkin:
// PHP: o'z hodisangizni nativephp kanaliga yuborasiz
// (event class ShouldBroadcastNow + Channel('nativephp') bilan)
// Frontend (har bir oynada):
// Echo.channel('nativephp').listen('SettingsUpdated', (e) => { /* UI'ni yangilash */ });
Bu uchala yo'l quyidagi diagrammada ko'rsatilgan:
SQLite/Eloquent bilan ma'lumot saqlash bo'yicha chuqurroq bilim uchun ../sql/README.md va ../laravel/README.md ga qarang.
Oyna hayot-tsikli hodisalari¶
NativePHP oyna holati o'zgarganda Laravel hodisalarini dispatch qiladi. Ular Native\Desktop\Events\Windows namespace'ida:
| Hodisa | Qachon |
|---|---|
WindowShown |
Oyna ko'rinadigan bo'lganda |
WindowFocused |
Oyna fokus olganda |
WindowBlurred |
Oyna fokusni yo'qotganda |
WindowMinimized |
Kichraytirilganda |
WindowMaximized |
Kattalashtirilganda |
WindowResized |
O'lcham o'zgarganda |
WindowHidden |
Yashirilganda |
WindowClosed |
Yopilganda |
Har bir hodisa oyna id'sini olib keladi (masalan WindowShown konstruktori public string $id). Bu hodisalar ShouldBroadcastNow ni amalga oshiradi va Channel('nativephp') ga broadcast bo'ladi β demak ularni ham PHP tarafda (Event::listen), ham frontend'da (Laravel Echo) tinglash mumkin.
PHP tarafda tinglash (masalan AppServiceProvider::boot() da):
use Illuminate\Support\Facades\Event;
use Native\Desktop\Events\Windows\WindowClosed;
Event::listen(function (WindowClosed $event) {
logger()->info('Oyna yopildi: ' . $event->id);
// Masalan, "settings" yopilganda biror narsani saqlash:
if ($event->id === 'settings') {
// ... holatni saqlash
}
});
Eslatma: bu hodisalar haqiqiy Electron jarayonida dispatch bo'ladi. Bu hujjat muhitida (displeysiz, Node/Electron'siz) ular amalda otilmaydi β bu kod to'g'ri, lekin uni ishlatib ko'rish uchun
native:devkerak (illustrativ).
Bularning hammasi qayerda yoziladi: oddiy controller¶
Amalda oyna ochish kodi controller, Livewire komponenti yoki maxsus servisda bo'ladi. Mana to'liq, haqiqatda tekshirilgan controller namunasi:
<?php
namespace App\Http\Controllers;
use Native\Desktop\Facades\Window;
class WindowDemoController extends Controller
{
public function openSettings()
{
Window::open('settings')
->title('Sozlamalar')
->route('settings.index')
->width(640)
->height(480)
->minWidth(400)
->minHeight(300)
->resizable(true);
return response()->json(['ok' => true]);
}
public function openAbout()
{
Window::open('about')
->title('Dastur haqida')
->route('about')
->width(420)
->height(320)
->resizable(false)
->maximizable(false);
return response()->json(['ok' => true]);
}
public function closeSettings()
{
Window::close('settings');
return response()->json(['ok' => true]);
}
public function reloadMain()
{
Window::reload('main');
return response()->json(['ok' => true]);
}
}
Route'lar:
use App\Http\Controllers\WindowDemoController;
Route::view('/settings', 'welcome')->name('settings.index');
Route::view('/about', 'welcome')->name('about');
Route::post('/win/settings/open', [WindowDemoController::class, 'openSettings']);
Route::post('/win/about/open', [WindowDemoController::class, 'openAbout']);
Route::post('/win/settings/close', [WindowDemoController::class, 'closeSettings']);
Route::post('/win/main/reload', [WindowDemoController::class, 'reloadMain']);
Oynani sinash: Window::fake()¶
Eng yoqimli jihatlardan biri β NativePHP Window facade'ini soxtalashtirib (fake), oyna haqiqatda ochilmasdan ham testdan o'tkazish mumkin. Bu real Electron'ni talab qilmaydi. Quyidagi testlarni men shu hujjat muhitida phpunit bilan haqiqatda ishga tushirib tekshirdim (5 ta test o'tdi).
Muhim nozik nuqta (
open()testi uchun): soxtaWindowManagerFake::open()o'zining qaytaradigan oyna obyektini sizdan kutadi. Agar test avvalWindow::fake()->alwaysReturnWindows([...])bilan oyna "urug'lab" (seed) qo'yilmasa,open()chaqirig'iWebmozart\Assert\InvalidArgumentException: No windows were provided to returnxatosini tashlaydi va controller500qaytaradi. Shuning uchun oyna ochadigan testlarda har bir ochiladiganiduchun bittanew \Native\Desktop\Windows\Window('id')obyektinialwaysReturnWindows([...])ga beramiz.close()/reload()testlari esa bunga muhtoj emas (ular oyna qaytarmaydi).
<?php
namespace Tests\Feature;
use Illuminate\Support\Facades\Http;
use Native\Desktop\Facades\Window;
use Native\Desktop\Windows\Window as WindowObject;
use Tests\TestCase;
class WindowDemoTest extends TestCase
{
public function test_settings_oynasi_ochiladi(): void
{
Http::fake(); // ichki API so'rovlarini ushlaymiz
// open() qaytaradigan oynani oldindan urug'laymiz:
Window::fake()->alwaysReturnWindows([
new WindowObject('settings'),
]);
$this->post('/win/settings/open')->assertOk();
Window::assertOpened('settings'); // 'settings' oynasi ochilganini tasdiqlash
}
public function test_settings_oynasi_yopiladi(): void
{
Window::fake(); // close() oyna qaytarmaydi β urug' shart emas
$this->post('/win/settings/close')->assertOk();
Window::assertClosed('settings');
}
public function test_main_qayta_yuklanadi(): void
{
Window::fake(); // reload() oyna qaytarmaydi β urug' shart emas
$this->post('/win/main/reload')->assertOk();
Window::assertReloaded('main');
}
}
Mavjud assert metodlari (paket fake'idan): assertOpened, assertClosed, assertHidden, assertShown, assertReloaded, assertNotReloaded, hamda assertOpenedCount, assertClosedCount va h.k. Ularning ba'zilari id o'rniga Closure ham qabul qiladi (masalan assertOpened(fn ($id) => str_starts_with($id, 'doc-'))).
Nima uchun
Http::fake()ham kerak? Chunki ochilgan oynagatitle()/url()kabi sozlamalar berilganda, ular ichki HTTP API'ga so'rov yuborishi mumkin.Http::fake()shu so'rovlarni ushlab, test'ni Electron'siz toza o'tkazadi.Eslatma (rasmiy hujjatdan farq): rasmiy testing hujjati
alwaysReturnWindows([...])gaMockerymock obyektini beradi. Bu yerda biz mock o'rniga to'g'ridan-to'g'ri haqiqiyNative\Desktop\Windows\Windowobyektini ishlatdik βfake'ningopen()metodi aynan shu turdagi (array<int, Window>) qiymatni kutadi, shu sababli qo'shimcha kutubxonasiz ham ishlaydi (yuqoridagi testlar shu usulda o'tdi).
Dev rejim va hot-reload¶
Ishlab chiqish vaqtida oynani har safar qo'lda yopib-ochishni xohlamaysiz. NativePHP dasturchi rejimida bu avtomatlashtirilgan:
native:dev ishga tushganda:
- Laravel ilovangiz va Electron qobiq birga ko'tariladi (illustrativ β displey + Node kerak).
- Blade/Livewire/route fayllarini o'zgartirsangiz, webview kontenti yangilanadi (oddiy veb-ishlab chiqishdagi kabi). Front-end uchun Vite HMR ham ishlaydi.
- Oyna konfiguratsiyasiga oid PHP o'zgarishlarini ko'rish uchun ko'pincha oynani
Window::reload(...)bilan qayta yuklaysiz yoki dev jarayonini qayta ishga tushirasiz.
Halol eslatma:
native:dev, real oyna ochilishi va hot-reload bu hujjat muhitida ishga tushirilmagan (displey, Electron va Node yo'q). Bu yerdagibashbuyruq va xulq tavsifi to'g'ri va rasmiy, lekin natija illustrativ. Aniq dev oqimi uchun: nativephp.com/docs/desktop/2/getting-started/development.
Tez-tez uchraydigan xatolar¶
// β Noto'g'ri namespace (eski/boshqa paket)
use Native\Laravel\Facades\Window; // v2 desktop'da bu YO'Q
// β
To'g'ri
use Native\Desktop\Facades\Window;
// β Bir xil ID'ni qayta ochib, "yangi oyna" kutish
Window::open('settings');
Window::open('settings'); // bu ikkinchi mustaqil oyna emas β ID bir xil
// β
Har bir alohida oyna uchun unikal ID
Window::open('settings');
Window::open('settings-2');
// β frameless() qilib, HTML'da drag zonasi qo'ymaslik
Window::open('w')->frameless(); // oynani ushlab ko'chirib bo'lmaydi!
// β
HTML'da -webkit-app-region: drag bilan ko'chirish zonasi qo'ying
Mashqlar¶
Oson¶
Windowfacade'ining to'g'ri namespace'ini yozing vamainoynasini ochadigan eng qisqa kodni keltiring.dashboardID'li oyna oching: sarlavhasi "Boshqaruv paneli", o'lchami 1024x720,dashboardnomli route'ni ko'rsatsin.aboutoynasini o'lchami o'zgarmas (resizable yo'q) va kattalashtirilmaydigan qilib oching.- Ochilgan
mainoynasini qayta yuklaydigan vasettingsoynasini yopadigan ikki qator kod yozing. timerID'li kichik (220x120) oynani doim boshqa oynalar ustida turadigan qilib oching.- Oynani
routeorqali emas, balki tashqi URL (https://nativephp.com) orqali ochish kodini yozing.
O'rta¶
- Ramkasiz va shaffof "suzuvchi vidjet" oyna oching (300x200). HTML tarafda oynani ushlab ko'chirish uchun qaysi CSS kerakligini ko'rsating.
editoroynasini oching: minimal o'lcham 600x400, maksimal 1600x1000, boshlang'ich 1024x720,rememberStatebilan.- Foydalanuvchi "Profil" tugmasini bossa,
profileoynasiniusers.showroute'iga['id' => 7]parametri bilan ochadigan controller metodini yozing. - NativePHP'da "modal" oyna metodi yo'q. "Modal kabi" tajriba beradigan oyna konfiguratsiyasini yozing va nima uchun bu haqiqiy modal emasligini izohlang.
WindowClosedhodisasiniAppServiceProvider::boot()da tinglab,settingsoynasi yopilganda log yozadigan kod yozing.Window::fake()yordamidadashboardoynasi ochilganini tekshiradigan feature test yozing (controller + route ham keltiring).
Qiyin¶
- Ikki oyna (
mainvasettings) o'rtasida mavzu (theme: dark/light) ma'lumotini almashishning uchta yo'lini (cache, route parametri, broadcast) kod bilan ko'rsating va har birining afzallik/kamchiligini bir jumlada izohlang. - Oyna
id'larini xavfsiz boshqarish uchun PHPenumyarating va uniWindow::open(...)da ishlating. Nima uchun bu "satrlarni qo'lda yozish"dan yaxshiroq? - Quyidagi talab uchun arxitekturani tushuntiring: foydalanuvchi bir vaqtning o'zida bir nechta hujjat oynasini (
doc-1,doc-2, ...) ochishi mumkin, har biri alohida ID bilan. Yangi hujjat oynasini ochish, hammasini ro'yxatlash (Window::all) vadoc-bilan boshlanadigan barcha oynalar ochilganini test'da tekshirish kodini yozing.
Yechimlar
1.
2.
Window::open('dashboard')
->title('Boshqaruv paneli')
->route('dashboard')
->width(1024)
->height(720);
3.
Window::open('about')
->title('Dastur haqida')
->route('about')
->resizable(false)
->maximizable(false);
4.
5.
6.
7.
Window::open('widget')
->width(300)
->height(200)
->frameless()
->transparent();
// yoki tayyor: Window::open('widget')->invisibleFrameless();
<div style="-webkit-app-region: drag; height: 32px;">Sarlavha</div>
<button style="-webkit-app-region: no-drag;">Yopish</button>
-webkit-app-region: drag β oynani ushlab ko'chirish zonasi; tugmalarda no-drag bo'lmasa, ularni bosib bo'lmaydi.
8.
Window::open('editor')
->title('Muharrir')
->route('editor')
->width(1024)
->height(720)
->minWidth(600)
->minHeight(400)
->maxWidth(1600)
->maxHeight(1000)
->rememberState();
9.
public function openProfile()
{
Window::open('profile')
->title('Profil')
->route('users.show', ['id' => 7])
->width(700)
->height(560);
return response()->json(['ok' => true]);
}
10. NativePHP Window facade'ida modal() metodi yo'q, shuning uchun uni ixtiro qilmaymiz. "Modal kabi" oyna:
Window::open('confirm')
->title('Tasdiqlang')
->route('confirm')
->width(360)->height(200)
->resizable(false)
->minimizable(false)
->maximizable(false)
->alwaysOnTop();
11.
// AppServiceProvider::boot()
use Illuminate\Support\Facades\Event;
use Native\Desktop\Events\Windows\WindowClosed;
Event::listen(function (WindowClosed $event) {
if ($event->id === 'settings') {
logger()->info('Sozlamalar oynasi yopildi');
}
});
12.
// Controller
public function openDashboard()
{
Window::open('dashboard')
->title('Boshqaruv paneli')
->route('dashboard')
->width(1024)->height(720);
return response()->json(['ok' => true]);
}
// Route
Route::view('/dashboard', 'welcome')->name('dashboard');
Route::post('/win/dashboard/open', [WindowDemoController::class, 'openDashboard']);
// Test
use Native\Desktop\Windows\Window as WindowObject;
public function test_dashboard_oynasi_ochiladi(): void
{
\Illuminate\Support\Facades\Http::fake();
// open() qaytaradigan oynani urug'laymiz (aks holda 500)
Window::fake()->alwaysReturnWindows([
new WindowObject('dashboard'),
]);
$this->post('/win/dashboard/open')->assertOk();
Window::assertOpened('dashboard');
Window::assertOpenedCount(1);
}
13. Uchta yo'l:
// 1) Cache (umumiy holat)
Cache::put('theme', 'dark'); // main oynasida o'rnatish
$theme = Cache::get('theme', 'light'); // settings oynasida o'qish
// + Sodda, hamma joyda ishlaydi. - "Real vaqtli" emas: o'zgarishni
// ikkinchi oyna o'zi qayta o'qishi/yuklanishi kerak.
// 2) Route parametri (ochishda uzatish)
Window::open('settings')->route('settings', ['theme' => 'dark']);
// + Ochish payti aniq kontekst beradi. - Faqat ochishda; oyna allaqachon
// ochiq bo'lsa, yangi qiymat o'z-o'zidan yetib bormaydi.
// 3) Broadcast hodisa (real vaqtli)
// event(new ThemeChanged('dark')); // 'nativephp' kanaliga broadcast
// Frontda: Echo.channel('nativephp').listen('ThemeChanged', e => apply(e.theme));
// + Barcha ochiq oynalar darhol yangilanadi. - Echo/broadcasting sozlash
// talab qiladi, biroz murakkabroq.
14.
enum WindowId: string
{
case Main = 'main';
case Settings = 'settings';
case About = 'about';
}
Window::open(WindowId::Settings->value)
->title('Sozlamalar')
->route('settings.index');
Window::close(WindowId::Settings->value);
'setings') kompilyatsiya/IDE bosqichida ushlaydi, (b) avtomatik to'ldirish (autocomplete) ishlaydi, (c) barcha ID'lar bitta joyda β refaktoring oson.
15. Arxitektura: har bir hujjat alohida oyna, ID doc-{n} shaklida. Oynalar bir xil PHP jarayonini bo'lishgani uchun ochiq hujjatlar ro'yxatini DB/cache yoki oddiy hisoblagichdan boshqaramiz. Window::all() ochiq oynalar ro'yxatini beradi.
class DocumentWindowController extends Controller
{
public function open(int $id)
{
Window::open("doc-{$id}")
->title("Hujjat #{$id}")
->route('documents.show', ['document' => $id])
->width(900)->height(680)
->rememberState();
return response()->json(['ok' => true]);
}
public function listOpen()
{
// Window::all() barcha ochiq oynalarni qaytaradi
$ids = collect(Window::all())->map(fn ($w) => $w->id)->all();
return response()->json(['windows' => $ids]);
}
}
doc- bilan boshlanadigan oynalar ochilganini Closure bilan tekshirish):
use Native\Desktop\Windows\Window as WindowObject;
public function test_hujjat_oynalari_ochiladi(): void
{
\Illuminate\Support\Facades\Http::fake();
// Har bir ochiladigan id uchun bitta urug' oyna:
Window::fake()->alwaysReturnWindows([
new WindowObject('doc-1'),
new WindowObject('doc-2'),
]);
$this->post('/documents/1/open')->assertOk();
$this->post('/documents/2/open')->assertOk();
// doc- bilan boshlanadigan oyna ochilganini tasdiqlash
Window::assertOpened(fn (string $id) => str_starts_with($id, 'doc-'));
Window::assertOpenedCount(2);
}
open() dan oldin Window::all() ni sanab, limitdan oshsa yangi oyna ochmaslik mantig'ini qo'shasiz.
β¬ οΈ Oldingi: 03 β Birinchi desktop ilova Β· π README Β· Keyingi: 05 β Menyu, tray va global yorliqlar β‘οΈ