التوثيق
الجهاز والبيئة
تتغير البيئة التي يعمل فيها التطبيق أثناء تشغيله: يتغير حجم النافذة (التدوير، العرض المقسّم، نافذة Mac)، أو ينتقل المستخدم إلى الوضع الداكن أو إلى حجم نص أكبر، أو تظهر لوحة المفاتيح وتختفي. يعرض NSH كل واحد من هذه عبر دالة وصول تفاعلية (reactive accessor): استدعها كما تستدعي signal، وكل ما قرأها (نمط أو نص أو قيمة محسوبة) يتحدث عند تغيّرها. لا توجد مستمعات لإضافتها أو إزالتها.
import { mount, signal, Text, View } from "nsh";
import { colorScheme, fontScale, pixelRatio, platform, windowSize } from "nsh";
function Row(props: { label: string; value: string }) {
return (
<View class="flex-row py-2 border-b border-[#e9e9e4] dark:border-[#1f2124]">
<Text class="flex-1 text-[#565a61] dark:text-[#8d9198]">{props.label}</Text>
<Text class="text-[#15161a] dark:text-[#eceef1]">{props.value}</Text>
</View>
);
}
function App() {
const hairline = () => 1 / pixelRatio();
const rtl = signal(false);
return (
<View class="flex-1 bg-[#f3f3ef] dark:bg-[#101113] px-4 pt-6 gap-4">
<Text class="text-2xl font-bold text-[#15161a] dark:text-[#eceef1]">Environment</Text>
<View class="rounded-2xl bg-white border border-[#e1e1db] dark:bg-[#18191c] dark:border-[#2c2e33] px-4 py-1">
<Row label="windowSize()" value={`${Math.round(windowSize().width)} × ${Math.round(windowSize().height)}`} />
<Row label="colorScheme()" value={colorScheme()} />
<Row label="fontScale()" value={fontScale().toFixed(2)} />
<Row label="platform.os" value={platform.os} />
<Row label="platform.version" value={platform.version} />
<Row label="platform.idiom" value={platform.idiom} />
<Row label="platform.desktop" value={String(platform.desktop)} />
<Row label="Locale" value={new Intl.DateTimeFormat().resolvedOptions().locale} />
<Row label="pixelRatio()" value={String(pixelRatio())} />
</View>
<Text class="text-[#565a61] dark:text-[#8d9198]">
{platform.select({ ios: "Running on iOS.", android: "Running on Android.", web: "Running in a browser.", default: "Running elsewhere." })}
</Text>
<View class="gap-1">
<View class="bg-[#8c9097] dark:bg-[#5a5e65]" style={{ height: hairline() }} />
<Text class="text-xs text-[#8c9097] dark:text-[#5a5e65]">The line above is one device pixel tall.</Text>
</View>
<View class={windowSize().width >= 600 ? "flex-row gap-3" : "gap-3"}>
<View class="flex-1 rounded-xl bg-[#4a6a0a] dark:bg-[#c8f26d] p-4">
<Text class="text-white dark:text-[#13160c] font-semibold">dark: classes follow the system</Text>
</View>
<View class="flex-1 rounded-xl bg-white border border-[#e1e1db] dark:bg-[#24262a] dark:border-[#2c2e33] p-4 hover:bg-zinc-50 dark:hover:bg-[#1d1e22] focus-visible:outline-2 focus-visible:outline-sky-500">
<Text class="text-[#15161a] dark:text-[#eceef1] font-semibold">A row only when the window is 600 pt or wider</Text>
</View>
</View>
<View
onPress={() => rtl.set(!rtl())}
activeStyle={{ opacity: 0.6 }}
style={{ direction: rtl() ? "rtl" : "ltr" }}
class="flex-row items-center gap-3 rounded-2xl bg-white border border-[#e1e1db] dark:bg-[#18191c] dark:border-[#2c2e33] p-4 hover:bg-zinc-50 dark:hover:bg-[#1d1e22] focus-visible:outline-2 focus-visible:outline-sky-500"
>
<Text class="text-lg text-[#4a6a0a] dark:text-[#c8f26d]">⇄</Text>
<Text class="flex-1 text-[#15161a] dark:text-[#eceef1]">Lay out right to left: {rtl() ? "on" : "off"}</Text>
</View>
</View>
);
}
mount(App);في Rust تكون دوال الوصول دوالَّ في nsh_core::prelude؛ وفي TSX تأتي من الوحدات المذكورة.
| Rust | TSX | القيمة |
|---|---|---|
window() | windowSize() (nsh) | عرض السطح وارتفاعه بالنقاط. |
dark() | colorScheme() (nsh) | الوضع الداكن أو الفاتح: وضع النظام، أو وضع التطبيق نفسه (set_color_scheme، setColorScheme). |
font_scale() | fontScale() (nsh) | تفضيل المستخدم لحجم النص نسبةً إلى الافتراضي (1 هو الافتراضي). |
safe_area() | safeArea() (nsh) | هوامش السطح التي لا تغطيها واجهة النظام: أعلى، يمين، أسفل، يسار. |
cx.insets() | safeAreaInsets() (nsh) | الهوامش في الموضع الذي يُعرض فيه المكوّن المستدعي (صفحة، حوار). |
keyboard_height()، keyboard_duration() | keyboardFrame() (nsh) | ارتفاع لوحة المفاتيح البرمجية ومدة حركتها. |
locale() | locale() (nsh) | لغة المستخدم ومنطقته (locale) كوسم BCP 47 ("en-US"، "zh-Hans-CN"): ما يُنسّق Intl وفقه افتراضيًا. |
lifecycle() | lifecycle() (nsh) | هل التطبيق قيد الاستخدام: active، inactive (ظاهر دون التركيز) أو background (في الخلفية). |
reduce_motion()، screen_reader() | reduceMotion()، screenReaderEnabled() (nsh) | انظر إمكانية الوصول. |
use nsh_core::prelude::*;
#[component]
pub fn App() -> impl IntoElement {
view()
.flex_1()
.pt(move || safe_area().top + px(16.0))
.px(px(16.0))
// A light and a dark value: the color follows the appearance natively.
.bg(AdaptiveColor::rgb(0xffffff, 0x0d1117))
.child(
text(move || {
let size = window();
format!(
"{} × {} pt, text at {:.2}×",
size.width.0.round(),
size.height.0.round(),
font_scale()
)
})
.text_color(AdaptiveColor::rgb(0x1f2328, 0xe6edf3)),
)
}import { mount, Text, View, safeArea } from "nsh";
import { colorScheme, fontScale, windowSize } from "nsh";
function App() {
return (
<View style={{ flex: 1, paddingTop: safeArea().top + 16, paddingHorizontal: 16, backgroundColor: colorScheme() === "dark" ? "#0d1117" : "#ffffff" }}>
<Text style={{ color: colorScheme() === "dark" ? "#e6edf3" : "#1f2328" }}>
{Math.round(windowSize().width)} × {Math.round(windowSize().height)} pt, text at {fontScale().toFixed(2)}×
</Text>
</View>
);
}
mount(App);#حجم النافذة والتخطيط المتجاوب
window() (TSX: windowSize()) هو حجم السطح الذي يرسم فيه NSH: الشاشة في الهاتف، والنافذة في iPad بالعرض المقسّم أو في Mac، والعرض المضيف عند تضمين NSH. استخدمه لتغيير التخطيط، لا الأحجام فقط:
use nsh_core::prelude::*;
fn gallery() -> impl IntoElement {
let columns = || {
let width = window().width.0;
if width >= 700.0 { 4.0 } else if width >= 500.0 { 3.0 } else { 2.0 }
};
let side = move || px((window().width.0 - 32.0 - (columns() - 1.0) * 8.0) / columns());
view().flex_row().flex_wrap().gap_2().child(each(
photos,
|p| p.id,
move |p, _| image(move || p.with(|p| p.url.clone())).w(side).aspect_ratio(1.0),
))
}import { Image, View } from "nsh";
import { windowSize } from "nsh";
function Gallery() {
const columns = () => (windowSize().width >= 700 ? 4 : windowSize().width >= 500 ? 3 : 2);
return (
<View style={{ flexDirection: "row", flexWrap: "wrap", gap: 8 }}>
<Each of={photos}>
{(p) => <Image src={p().url} style={{ width: (windowSize().width - 32 - (columns() - 1) * 8) / columns(), aspectRatio: 1 }} />}
</Each>
</View>
);
}غالبًا ما تكفي flexbox والنسب المئوية وaspect_ratio (aspectRatio) دون قراءة الحجم إطلاقًا؛ انظر التخطيط. تبدّل style_when(|| window().width.0 >= 640.0, |s| s.flex_row()) في Rust الأنماط بحسب العرض؛ أما في TSX مع Tailwind فمتغيرات نقاط التوقف (sm:، md:، lg:، xl:، 2xl:، max-*:، min-[…]:) وportrait: / landscape: تتبع النافذة أيضًا: class="grid grid-cols-2 md:grid-cols-4".
#الوضع الداكن
يتبع المظهر النظام ما لم يضبط التطبيق مظهره الخاص. في Rust أبسط طريقة لدعم الوضع الداكن هي لون بقيمة فاتحة وأخرى داكنة، AdaptiveColor::rgb(light, dark): يتبدل أصليًا عند تبدل المظهر، دون أي شيء يجب تشغيله. وفي TSX هو متغير Tailwind dark:، الذي يُطبَّق عندما تكون colorScheme() هي "dark":
view()
.flex_1()
.p_4()
.bg(AdaptiveColor::rgb(0xffffff, 0x18181b))
.child(
text("Follows the system appearance")
.text_color(AdaptiveColor::rgb(0x18181b, 0xf4f4f5)),
)<View class="flex-1 bg-white dark:bg-zinc-900 p-4">
<Text class="text-zinc-900 dark:text-zinc-100">Follows the system appearance</Text>
</View>لأي حالة أخرى، اقرأ dark() (TSX: colorScheme()) داخل closure أو قيمة محسوبة؛ وفي TSX، إن زاد عدد الألوان على بضعة ألوان، فاشتقّ لوحة ألوان مرة واحدة:
use nsh_core::prelude::*;
// `dark()` is reactive: the closure runs again when the appearance changes.
view()
.flex_1()
.bg(move || if dark() { rgb(0x0d1117) } else { rgb(0xf6f8fa) })
.child(text("Hello"))import { computed, Text, View } from "nsh";
import { colorScheme } from "nsh";
const palette = computed(() =>
colorScheme() === "dark"
? { background: "#0d1117", surface: "#161b22", text: "#e6edf3", muted: "#8b949e" }
: { background: "#f6f8fa", surface: "#ffffff", text: "#1f2328", muted: "#57606a" },
);
<View style={{ flex: 1, backgroundColor: palette().background }}>
<Text style={{ color: palette().text }}>Hello</Text>
</View>تأخذ مكوّنات Rust ألوانها أيضًا من السمة (theme) التي لها قيم فاتحة وداكنة (nsh.json "theme"، انظر البناء باستخدام Rust)، لذا تتبدل مع النظام كذلك. لمطابقة شريط الحالة مع خلفيتك، امنح صفحاتك وحواراتك statusBar، أو امنح التطبيق واحدًا في nsh.json.
#تطبيق بمظهره الخاص
التطبيق الذي يكون داكنًا دائمًا (أو فاتحًا دائمًا) يصرّح بذلك في nsh.json، فتتبعه dark() وcolorScheme() والألوان التكيفية وفئات dark: والسمة والنمط الافتراضي لشريط الحالة والتنبيهات منذ الإطار الأول (وتُطلَق التطبيقات المحزَّمة به أيضًا):
{ "appearance": "dark" }
يغيّره set_color_scheme(Appearance::Dark) (TSX: setColorScheme من nsh) أثناء التشغيل، لإعداد مظهر داخل التطبيق؛ ويعيد Appearance::System ("system") اتباع النظام مجددًا:
use nsh_core::prelude::*;
view()
.on_press(move || {
set_color_scheme(if dark() { Appearance::Light } else { Appearance::Dark })
})
.role(AccessibilityRole::Button)
.rounded_lg()
.px_4()
.py_2()
.bg(AdaptiveColor::rgb(0xe5e5e5, 0x262626))
.child(
text(|| if dark() { "Light" } else { "Dark" })
.text_color(AdaptiveColor::rgb(0x171717, 0xfafafa)),
)import { Text, View } from "nsh";
import { colorScheme, setColorScheme } from "nsh";
<View onPress={() => setColorScheme(colorScheme() === "dark" ? "light" : "dark")} role="button" class="rounded-lg px-4 py-2 bg-neutral-200 dark:bg-neutral-800">
<Text class="text-neutral-900 dark:text-neutral-50">{colorScheme() === "dark" ? "Light" : "Dark"}</Text>
</View>#حجم النص
font_scale() (TSX: fontScale()) هو إعداد حجم النص لدى المستخدم (Dynamic Type في iOS، وحجم الخط في Android) نسبةً إلى الافتراضي. يتبعه النص وحقول الإدخال تلقائيًا: يُعرض fontSize: 16 أكبر عندما يطلب المستخدم نصًا أكبر. ويمكنك لكل عنصر إلغاء ذلك أو وضع حدّ أقصى له:
Rust (على text وinput) | TSX (على <Text> و<Input>) | الافتراضي | الوصف |
|---|---|---|---|
.allow_font_scaling(..) | allowFontScaling | true | اتباع حجم نص النظام. |
.max_font_size_multiplier(..) | maxFontSizeMultiplier | 0 (بلا حد) | أكبر مقياس يُطبَّق على هذا النص. |
text("Today")
.text_size(px(34.0))
.font_weight(FontWeight::BOLD)
.max_font_size_multiplier(1.4)<Text style={{ fontSize: 34, fontWeight: "700" }} maxFontSizeMultiplier={1.4}>Today</Text>اقرأ font_scale() لتكييف التخطيط الذي لا يتبع النص تلقائيًا، مثل تحويل صف إلى عمود عندما يكبر النص: .style_when(|| font_scale() > 1.3, |s| s.flex_col()) في Rust، وflexDirection: fontScale() > 1.3 ? "column" : "row" في TSX.
يغيّر حجم النص مقياس النص: كل نص وحقل إدخال يسمح بتحجيم الخط، وتنبيهات المنصة ورسائلها المنبثقة (toasts). أما الأطوال فتحتفظ بحجمها: المسافات والعروض والارتفاعات وأنصاف أقطار الزوايا والحدود والصور، سواء بالنقاط أو بالفئات (p-4، rounded-lg، w-[2rem]). وتكبر الصناديق التي يحدد محتواها حجمها مع نصها.
#اللغة والمنطقة
locale() هي لغة المستخدم ومنطقته (locale) كوسم BCP 47 (locale().as_str() في Rust). في TSX يُنسّق Intl الأرقام والتواريخ وفقها افتراضيًا، فيعطي new Intl.NumberFormat().format(1234.5) القيمة 1,234.5 في en-US و1.234,5 في de-DE. اقرأ locale() لاختيار نصوص ترجماتك الخاصة: تتحدث النصوص التي تقرؤها عند تغيّرها.
use nsh_core::prelude::*;
fn greeting() -> &'static str {
match locale().as_str().split('-').next() {
Some("de") => "Willkommen",
Some("zh") => "欢迎",
_ => "Welcome",
}
}
text(greeting)import { Text } from "nsh";
import { locale } from "nsh";
const greetings: Record<string, string> = { en: "Welcome", de: "Willkommen", zh: "欢迎" };
<Text>{greetings[locale().split("-")[0]] ?? greetings.en}</Text>#في تطبيق مُضمِّن
يتبع التطبيق المعروض داخل تطبيق أصلي (native) إعدادات ذلك التطبيق إن ضبطها: تحل لغة واجهات التضمين البرمجية وحجم نصها (AppView::set_locale وset_font_scale في Rust على GPUI وmacOS وLinux وWindows، وlocale وfontScale في Swift على iOS، وsetLocale وsetFontScale في Java على Android) محل لغة النظام وحجم نصه. تتبعها locale() وIntl وfontScale() والنص فورًا، كما تتبع تغيير النظام، وتعود قيم النظام عندما يمسحها التطبيق المُضمِّن. استخدمها عندما يتيح التطبيق الأصلي لمستخدميه اختيار لغة أو حجم نص خاص بهم.
#في الخلفية
يخبر lifecycle() (وهو نفسه في TSX) ما إذا كان المستخدم يستخدم التطبيق. قيمته active في المقدمة، وinactive عندما يظل التطبيق ظاهرًا لكن تطبيقًا أو نافذة أخرى تملك التركيز (أو يعرض النظام شيئًا فوقه: مبدّل التطبيقات، مكالمة واردة)، وbackground عندما لا يكون ظاهرًا: في الخلفية على الهاتف، أو مصغّرًا على سطح المكتب، أو علامة تبويب مخفية على الويب. وكما هي الحال مع كل دالة وصول، يحدّث ما قرأه، فيوقف effect بثًا مباشرًا أو استطلاعًا دوريًا أو فيديو مؤقتًا في الخلفية ويستأنفها عندما يعود المستخدم:
effect(move || {
if lifecycle() == Lifecycle::Background {
feed.pause();
} else {
feed.resume();
}
});effect(() => {
if (lifecycle() === "background") feed.pause();
else feed.resume();
});استخدم background لإيقاف العمل مؤقتًا، واحفظ ما يغيّره المستخدم لحظة تغييره: فقد يعلّق النظام التطبيق بعد ثوانٍ من انتقاله إلى الخلفية، قبل أن يعمل effect يقرأ التغيير. يتبع التطبيق المعروض داخل تطبيق أصلي حالة ذلك التطبيق، وحيث يكون للتطبيق عدة نوافذ (iPad، سطح مكتب) يتبع العرض النافذة التي هو فيها. في الاختبارات، ينقل app.setLifecycle("background") من nsh/test التطبيق إلى هناك (الاختبار).
#المناطق الآمنة
تغطي الشقوق (notches) ومؤشر الشاشة الرئيسية وشريط الحالة وشريط التنقل في Android وشريط عنوان Mac Catalyst أجزاءً من السطح. ترسم تطبيقات NSH من حافة إلى حافة على كل منصة، وتحصل على الأجزاء المغطاة في صورة هوامش (insets).
safe_area()(TSX:safeArea()) يعطي هوامش السطح.cx.insets()الخاص بالمكوّن (TSX:safeAreaInsets()) يعطي الهوامش في الموضع الذي يُعرض فيه المكوّن: داخل صفحة تنقّل تُقاس أسفل ترويستها، وداخل حوار تكون هوامش الحوار. فضّله في المكوّنات التي قد تظهر في أكثر من موضع.- تضيف
env(safe-area-inset-top)و-rightو-bottomو-leftفي CSS حشوة أو مسافة لأي عرض بمقدار الهوامش في موضع عرضه، داخلpadding*وmargin*(يضيفcalc(env(safe-area-inset-bottom) + 8px)طولًا؛ وفي Tailwindpt-[env(safe-area-inset-top)]؛ وفي Rust.p_safe_area()و.pt_safe_area()و.pb_safe_area_plus(px(8.0))، …). تتحدث الحشوة أصليًا عند تغيّر الهوامش (المنطقة الآمنة).
use nsh_core::prelude::*;
fn screen() -> impl IntoElement {
view()
.pt_safe_area()
.px_safe_area()
.flex_1()
.bg(white())
.child(content())
.child(bottom_bar())
}
// A component, for its `Scope`: the insets where it renders.
#[component]
fn BottomBar(cx: Scope<NoEvent>) -> impl IntoElement {
// `cx.insets()`: the insets where the component renders.
view()
.flex_row()
.pt(px(8.0))
.pb(move || cx.insets().bottom + px(8.0))
}import { View, safeAreaInsets } from "nsh";
function Screen() {
return (
<View class="flex-1 bg-white pt-[env(safe-area-inset-top)] pr-[env(safe-area-inset-right)] pl-[env(safe-area-inset-left)]">
<Content />
<BottomBar />
</View>
);
}
function BottomBar() {
const insets = safeAreaInsets();
return <View style={{ paddingBottom: insets().bottom + 8, paddingTop: 8, flexDirection: "row" }}>…</View>;
}في Android، لا يحصل العرض المضمَّن أسفل شريط أدوات إلا على أجزاء أشرطة النظام التي تتداخل معه، كما في المنطقة الآمنة في UIKit: لا هامش علوي إن لم يصل إلى شريط الحالة.
#المنصة
تصف os() وos_version() وidiom() وis_desktop() وis_catalyst() في Rust، والثابت platform من nsh في TSX، المكان الذي يعمل فيه التطبيق. وتبقى ثابتة طوال تشغيل التطبيق.
| Rust | TSX | الوصف |
|---|---|---|
os() | platform.os | Os::Ios، Os::Android، Os::MacOs، Os::Windows، Os::Linux أو Os::Web (os().name() هي السلسلة النصية في TSX). Mac Catalyst هو iOS. |
os_version() | platform.version | الرقمان الرئيسي والفرعي؛ وفي Android مستوى API و0. |
idiom() | platform.idiom | Idiom::Phone، Pad، Mac، Tv أو Desktop؛ Desktop تمامًا حيث يتحقق is_desktop(). |
is_desktop() | platform.desktop | macOS أو Windows أو Linux، أو متصفح سطح مكتب (سطح المكتب والجوال). |
is_catalyst() | platform.catalyst | Mac Catalyst، في أي من نمطيه (idiom). |
platform في TSX كاملًا:
| العضو | الوصف |
|---|---|
platform.os | "ios"، "android"، "macos"، "windows"، "linux" أو "web". يُبلّغ Mac Catalyst عن "ios". |
platform.version | سلسلة نصية: في iOS وأنظمة سطح المكتب: إصدار نظام التشغيل ("17.5")؛ في Android: مستوى API ("35")؛ على الويب: "0.0". |
platform.idiom | "phone"، "pad"، "mac"، "tv" أو "desktop". "desktop" تمامًا حيث يكون platform.desktop هو true. "mac" هو نمط Mac في UIKit تحت Mac Catalyst، وهو نمط جوال. |
platform.desktop | ينطبق سلوك سطح المكتب: macOS وWindows وLinux ومتصفحات سطح المكتب. تتبعه خصائص desktop={{…}} وplatform.select({ desktop }). |
platform.catalyst | يعمل التطبيق تحت Mac Catalyst (os "ios")، بنمط Mac أو iPad. |
platform.select(spec) | القيمة لهذه المنصة؛ انظر الشيفرة الخاصة بالمنصة. |
use nsh_core::{element::Os, prelude::*};
let haptics_supported = os() == Os::Ios || (os() == Os::Android && os_version().0 >= 30);import { platform } from "nsh";
const hapticsSupported = platform.os === "ios" || (platform.os === "android" && Number(platform.version) >= 30);يعمل nsh test دون واجهة (headless) بوصفه iOS.
على Linux، تكون platform.os هي "linux"، والنمط "desktop"، وplatform.version هو الإصدار الرئيسي والفرعي للنواة ("6.12"): فالتوزيعات ترقّم إصداراتها بطرق لا يمكن مقارنتها. وهناك تكون المنطقة الآمنة وارتفاع لوحة المفاتيح 0، ويتبع colorScheme() إعداد سطح المكتب (XDG settings portal) مباشرةً، ويتبع fontScale() تحجيم النص في سطح المكتب، ويتبع pixelRatio() مقياس الشاشة، بما فيه المقاييس الكسرية.
على macOS (واجهة AppKit الخلفية)، تكون platform.os هي "macos"، والنمط "desktop"، وplatform.version هو إصدار macOS. المنطقة الآمنة وارتفاع لوحة المفاتيح 0، ويتبع colorScheme() مظهر النظام مباشرةً، وقيمة fontScale() هي 1، وpixelRatio() هو مقياس الدعم (backing scale) للشاشة.
على Windows، تكون platform.os هي "windows"، والنمط "desktop"، وplatform.version هي "10.0"، في Windows 11 أيضًا (فقد أبقى Windows رقم الإصدار). المنطقة الآمنة وارتفاع لوحة المفاتيح 0، ويتبع colorScheme() Settings > Personalization > Colors مباشرةً، ويتبع fontScale() إعداد حجم النص (من 1 إلى 2.25)، ويتبع reduceMotion() إعداد تأثيرات الحركة، ويتبع pixelRatio() مقياس الشاشة، ويُنسّق Intl وفق التنسيق الإقليمي للمستخدم.
في المتصفح (بيئة تشغيل الويب)، تكون platform.os هي "web" وplatform.version هي "0.0". يكون النمط "desktop" مع الفأرة ونافذة بعرض 600 نقطة على الأقل، و"pad" على الجهاز اللوحي، و"phone" على الهاتف، ويُحدَّد عند بدء التطبيق. أما اللغة (locale) وreduceMotion() وfontScale() (حجم نص المتصفح) وpixelRatio() والمنطقة الآمنة وارتفاع لوحة المفاتيح فتأتي من المتصفح وتتبعه مباشرةً.
#كثافة البكسل
تحوّل pixel_ratio() وround_to_nearest_pixel(points) في Rust، وpixelRatio() من nsh في TSX، بين النقاط وبكسلات الجهاز.
| Rust | TSX | الوصف |
|---|---|---|
pixel_ratio() | pixelRatio() | بكسلات الجهاز لكل نقطة (2 أو 3 في معظم الهواتف)، تفاعلية. |
round_to_nearest_pixel(points) | Math.round(x * pixelRatio()) / pixelRatio() | النقاط مقرّبة إلى أقرب بكسل جهاز. |
خط رفيع (hairline) بارتفاع بكسل جهاز واحد:
view()
.h(move || px(1.0 / pixel_ratio()))
.bg(rgb(0xd0d7de))import { View } from "nsh";
import { pixelRatio } from "nsh";
<View style={{ height: 1 / pixelRatio(), backgroundColor: "#d0d7de" }} />pixel_ratio() وpixelRatio() تفاعليتان، لذا يتبع الخط الرفيع نافذةً نُقلت إلى شاشة ذات كثافة أخرى.
#من اليمين إلى اليسار
عندما تُكتب لغة الجهاز من اليمين إلى اليسار (العربية، العبرية، الفارسية، …)، يرتّب NSH التطبيق من اليمين إلى اليسار: تمتد الصفوف من اليمين، وتأخذ خصائص النمط المنطقية جهاتها في الاتجاه من اليمين إلى اليسار، ويُحاذى النص إلى اليمين، وتنعكس الأدوات الأصلية. يتبع اتجاه التخطيط لغة المستخدم تلقائيًا. ويحدد نمط direction للعرض ("ltr" أو "rtl" أو "inherit"؛ .direction(LayoutDirection::..) في Rust) اتجاه شجرته الفرعية؛ وعلى العرض الجذر يحدد اتجاه التطبيق.
تُطبَّق التغييرات فورًا دون إعادة تشغيل التطبيق؛ والعرض التوضيحي في هذه الصفحة يبدّل نمط direction.
اكتب تخطيطات تعمل في الاتجاهين باستخدام الخصائص المنطقية: في Rust ms / me وps / pe (بأسماء Tailwind)، وفي TSX marginStart / marginEnd وpaddingStart / paddingEnd وstart / end وborderTopStartRadius وtextAlign: "start" وغيرها، وهي تعني اليسار واليمين في التخطيطات من اليسار إلى اليمين، والعكس في التخطيطات من اليمين إلى اليسار. أما left وright (marginLeft وpaddingRight وleft وtextAlign: "left" …) فتحتفظان بجهتيهما في الاتجاهين، كما في CSS. ويتجاوز نمط direction ("inherit" و"ltr" و"rtl") الاتجاه لشجرة فرعية، مثلًا للإبقاء على رقم هاتف أو أزرار مشغّل وسائط من اليسار إلى اليمين. وتُطبَّق متغيرات Tailwind rtl: وltr: بحسب الاتجاه.
تبدأ عروض التمرير الأفقي والقوائم من الطرف الأيمن في التخطيطات من اليمين إلى اليسار. وفي Android يجب أن يصرّح التطبيق المضيف بـandroid:supportsRtl="true" في ملف manifest الخاص به كي تنعكس الأدوات الأصلية.
#ذات صلة
- واجهة الجهاز في
nsh: المرجع. - الشيفرة الخاصة بالمنصة:
platform.select، وخصائص المنصة ووحداتها. - إمكانية الوصول: قارئات الشاشة وتقليل الحركة.
- المنطقة الآمنة ولوحة المفاتيح وشريط الحالة.
- Tailwind:
dark:، ومتغيرات نقاط التوقف والاتجاه.