melawy-plymouth-theme-neon-.../melawy-neon-recline/script.script

846 lines
27 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Theme Name: melawy-plymouth-theme-neon-recline (neon-recline)
Version: 3.0
Description: Plymouth boot splash theme for Melawy OS
Author: Valeria Fadeeva <valeria.fadeeva.me+melawy@gmail.com>
https://github.com/Valeria-Fadeeva
https://github.com/Melawy
https://gitlab.com/melawy/
Date: 2026.07.24
License: GNU AFFERO GENERAL PUBLIC LICENSE, see <http://www.gnu.org/licenses/>
*/
// ============================================================
// УТИЛИТЫ
// ============================================================
Window.GetMaxWidth = fun() {
i = 0;
width = 0;
while (Window.GetWidth(i)) {
width = Math.Max(width, Window.GetWidth(i));
i++;
}
return width;
};
Window.GetMaxHeight = fun() {
i = 0;
height = 0;
while (Window.GetHeight(i)) {
height = Math.Max(height, Window.GetHeight(i));
i++;
}
return height;
};
Percent = fun(perc, pixels) {
return Math.Int(Math.Abs(Math.Int(pixels)) / 100 * Math.Abs(Math.Int(perc)));
};
Limit = fun(width, height, perc) {
local.this;
this.width = width;
this.height = height;
this.perc = perc;
this.GetWidth = fun() {
return Percent(this.perc, this.width);
};
this.GetHeight = fun() {
return Percent(this.perc, this.height);
};
return this;
};
ScaleImage = fun(image1, image2) {
local.img1_w = image1.GetWidth();
local.img1_h = image1.GetHeight();
local.img1_ratio = img1_w / img1_h;
local.img2_w = image2.GetWidth();
local.img2_h = image2.GetHeight();
local.img2_ratio = img2_w / img2_h;
// Если изображение уже маленькое или квадратное — масштабируем напрямую
if (img1_ratio == 1 || (img1_w < 200 && img1_h < 200)) {
return image1.Scale(img2_w, img2_h);
}
// Размеры совпадают — ничего не делаем
if (img2_w == img1_w && img2_h == img1_h) {
return image1;
}
// Сохраняем пропорции
local.factor;
if (img2_ratio < img1_ratio) {
factor = img2_h / img1_h;
} else {
factor = img2_w / img1_w;
}
return image1.Scale(Math.Int(img1_w * factor), Math.Int(img1_h * factor));
};
FitIntoDimensions = fun(image1, image2) {
local.img1_w = image1.GetWidth();
local.img1_h = image1.GetHeight();
local.img2_w = image2.GetWidth();
local.img2_h = image2.GetHeight();
local.ma = Math.Max(img1_w, img1_h);
local.mi = Math.Min(img2_w, img2_h);
if (mi == 0) return image1;
local.factor = ma / mi;
return image1.Scale(Math.Int(img1_w / factor), Math.Int(img1_h / factor));
};
SetSpriteImage = fun(asset, x, y, z) {
local.sprite = Sprite();
local.img = Image(asset);
sprite.SetImage(img);
sprite.SetPosition(x, y, z);
return sprite;
};
// Улучшенный blur: больше слоёв с меньшим смещением для более плавного эффекта
Blur = fun(background_src, foreground_src, x, y, z, width, height, scale_width, scale_height) {
local.result = [];
// --- Фон (размытие через многослойное наложение) ---
local.bg_image = Image(background_src);
bg_image = ScaleImage(bg_image, Limit(scale_width, scale_height, 100));
bg_image = bg_image.Crop(x, y, width, height);
bg_image = ScaleImage(bg_image, Limit(Percent(120, width), Percent(120, height), 100));
bg_image = bg_image.Crop(x, y, width, height);
// Создаём 9 слоёв для более качественного размытия (3x3 сетка со смещением ±1)
local.blur_layers = [
[-1, -1], [0, -1], [1, -1],
[-1, 0], [0, 0], [1, 0],
[-1, 1], [0, 1], [1, 1]
];
local.layer_opacity = 1.0 / 9.0;
local.i;
for (i = 0; i < 9; i++) {
result[i].sprite = Sprite(bg_image);
result[i].sprite.SetOpacity(layer_opacity);
result[i].sprite.SetPosition(
x + blur_layers[i][0],
y + blur_layers[i][1],
z + i
);
}
// --- Передний план ---
local.fg_image = Image(foreground_src);
fg_image = fg_image.Scale(width, height);
result[9].sprite = Sprite(fg_image);
result[9].sprite.SetOpacity(0.75);
result[9].sprite.SetPosition(x, y, z + 9);
return result;
};
// ============================================================
// ШРИФТЫ И ТЕКСТ (масштабируются от высоты экрана)
// ============================================================
screen.width = Window.GetMaxWidth();
screen.height = Window.GetMaxHeight();
aspect_ratio = screen.width / screen.height;
// Базовый размер шрифта зависит от высоты экрана
base_font_size = Math.Max(Math.Int(screen.height / 110), 10);
if (base_font_size > 32) base_font_size = 32;
common_font = "Noto Sans Mono Regular " + base_font_size;
progress_font = "Noto Sans Mono Regular " + Math.Int(base_font_size * 1.2);
link_font = "Noto Sans Italic " + Math.Int(base_font_size * 1.2);
common_symbol.image = Image.Text("M", 0.5, 0.5, 0.5, 1, common_font);
common_symbol.height = common_symbol.image.GetHeight();
common_symbol.width = common_symbol.image.GetWidth();
progress_symbol.image = Image.Text("M", 0.5, 0.5, 0.5, 1, progress_font);
progress_symbol.height = progress_symbol.image.GetHeight();
progress_symbol.width = progress_symbol.image.GetWidth();
link_symbol.image = Image.Text("M", 0.5, 0.5, 0.5, 1, link_font);
link_symbol.height = link_symbol.image.GetHeight();
link_symbol.width = link_symbol.image.GetWidth();
starting_text = "Starting up…";
bye_text = "System is shutting down";
progress_t = 0;
x0 = 0;
y0 = 0;
z0 = 0;
Window.SetBackgroundTopColor(0, 0, 0);
Window.SetBackgroundBottomColor(0, 0, 0);
// ============================================================
// ФОН
// ============================================================
if (aspect_ratio == 3440 / 1440) {
background.image = Image("0EkLvkiE_4g_3440x1440.png");
} else if (aspect_ratio == 1920 / 1080) {
background.image = Image("0EkLvkiE_4g_2564x1440.png");
} else {
background.image = Image("BACKGROUND.png");
}
background.image = ScaleImage(background.image, Limit(screen.width, screen.height, 100));
background.width = background.image.GetWidth();
background.height = background.image.GetHeight();
background.x = x0 + screen.width / 2 - background.width / 2;
background.y = y0 + screen.height / 2 - background.height / 2;
background.sprite = Sprite();
background.sprite.SetImage(background.image);
background.sprite.SetOpacity(1);
background.sprite.SetPosition(background.x, background.y, 0);
// ============================================================
// БОКОВАЯ ПАНЕЛЬ (BLUR)
// ============================================================
side_panel.background_src = "BACKGROUND.png";
side_panel.foreground_src = "SIDE_BACKGROUND2.png";
side_panel.x = x0;
side_panel.y = y0;
side_panel.z = 1;
if (aspect_ratio == 3440 / 1440) {
side_panel.width = Percent(25, screen.width);
} else if (aspect_ratio == 1920 / 1080) {
side_panel.width = Percent(33, screen.width);
} else {
side_panel.width = Percent(33, screen.width);
}
side_panel.height = Percent(100, screen.height);
blur_result = Blur(
side_panel.background_src,
side_panel.foreground_src,
side_panel.x, side_panel.y, side_panel.z,
side_panel.width, side_panel.height,
screen.width, screen.height
);
// ============================================================
// БЛОК СООБЩЕНИЙ
// ============================================================
side_block.width = 0;
side_block.height = 0;
messages_box.image = Image("MESSAGES_BOX.png");
messages_box.image = messages_box.image.Scale(
Percent(80, side_panel.width),
Percent(50, side_panel.height)
);
messages_box.width = messages_box.image.GetWidth();
messages_box.height = messages_box.image.GetHeight();
messages_box.x = Percent(50, side_panel.width) - Percent(50, messages_box.width);
// messages_box.y = Percent(aspect_ratio, side_panel.height);
messages_box.y = 0;
messages_box.sprite = Sprite(messages_box.image);
messages_box.sprite.SetOpacity(1);
messages_box.sprite.SetPosition(messages_box.x, messages_box.y, 12);
side_block.width = messages_box.width;
side_block.height = messages_box.height;
// ============================================================
// ФОРМА ПРОГРЕССА
// ============================================================
progress_form.image = Image("PROGRESS_FORM.png");
progress_form.image = progress_form.image.Scale(
Percent(80, side_panel.width),
Percent(15, side_panel.height)
);
progress_form.width = progress_form.image.GetWidth();
progress_form.height = progress_form.image.GetHeight();
progress_form.x = Percent(50, side_panel.width) - Percent(50, progress_form.width);
progress_form.y = messages_box.y + messages_box.height + Percent(aspect_ratio, side_panel.height);
progress_form.sprite = Sprite(progress_form.image);
progress_form.sprite.SetOpacity(1);
progress_form.sprite.SetPosition(progress_form.x, progress_form.y, 12);
side_block.height = side_block.height + Percent(aspect_ratio, side_panel.height) + progress_form.height;
// ============================================================
// СПИННЕР
// ============================================================
spinner.image = Image("SPINNER.png");
spinner.image = spinner.image.Scale(
Percent(1, screen.width),
Percent(1, screen.width)
);
spinner.width = spinner.image.GetWidth();
spinner.height = spinner.image.GetHeight();
spinner.x = progress_form.x + progress_form.width - Percent(10, progress_form.width);
spinner.y = progress_form.y + Percent(10, progress_form.height);
spinner.sprite = Sprite(spinner.image);
spinner.sprite.SetPosition(spinner.x, spinner.y, 13);
// ============================================================
// ТЕКСТ ПРОГРЕССА (%)
// ============================================================
progress_text.image = Image.Text(progress_t + "%", 0.5, 0.5, 0.5, 1, progress_font);
progress_text.width = progress_text.image.GetWidth();
progress_text.height = progress_text.image.GetHeight();
progress_text.x = progress_form.x + Percent(5, progress_form.width);
progress_text.y = progress_form.y + Percent(10, progress_form.height);
progress_text.sprite = Sprite();
progress_text.sprite.SetImage(progress_text.image);
progress_text.sprite.SetPosition(progress_text.x, progress_text.y, 13);
// ============================================================
// БЛОК ПРОГРЕССА (РАМКА)
// ============================================================
progress_box.image = Image("PROGRESS_BOX.png");
progress_box.image = progress_box.image.Scale(
Percent(90, progress_form.width),
Percent(15, progress_form.height)
);
progress_box.width = progress_box.image.GetWidth();
progress_box.height = progress_box.image.GetHeight();
progress_box.x = progress_form.x + (progress_form.width - progress_box.width) / 2;
progress_box.y = progress_form.y + (progress_form.height - progress_box.height) / 2;
progress_box.sprite = Sprite(progress_box.image);
progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 13);
// ============================================================
// ПОЛОСКА ПРОГРЕССА
// ============================================================
progress_bar.original_image = Image("PROGRESS_BAR.png");
progress_bar.original_image = progress_bar.original_image.Scale(
Percent(90, progress_form.width),
Percent(15, progress_form.height)
);
progress_bar.width = progress_bar.original_image.GetWidth();
progress_bar.height = progress_bar.original_image.GetHeight();
progress_bar.x = progress_form.x + (progress_form.width - progress_bar.original_image.GetWidth()) / 2;
progress_bar.y = progress_form.y + (progress_form.height - progress_bar.original_image.GetHeight()) / 2;
progress_bar.sprite = Sprite();
progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 14);
// ============================================================
// FADE ЭФФЕКТ НА ПРОГРЕССЕ
// ============================================================
progress_fade.image = Image("PROGRESS_FADE.png");
progress_fade.image = progress_fade.image.Scale(
Percent(10, progress_form.width),
Percent(15, progress_form.height)
);
progress_fade.width = progress_fade.image.GetWidth();
progress_fade.height = progress_fade.image.GetHeight();
progress_fade.sprite = Sprite(progress_fade.image);
max_counter = progress_box.width - progress_fade.width;
// Глобальные переменные анимации fade
counter = 0;
fade_dir = 0; // 0 = вправо, 1 = влево
// ============================================================
// CALLBACK: ПРОГРЕСС ЗАГРУЗКИ
// ============================================================
fun progress_callback(duration, progress) {
local.new_width = Math.Int(progress_bar.original_image.GetWidth() * progress);
if (progress_bar.image.GetWidth() != new_width) {
progress_bar.image = progress_bar.original_image.Scale(
new_width,
progress_bar.original_image.GetHeight()
);
progress_bar.sprite.SetImage(progress_bar.image);
local.f = Math.Int(progress * 100);
progress_t.image = Image.Text(f + "%", 0.5, 0.5, 0.5, 1, progress_font);
progress_text.sprite.SetImage(progress_t.image);
}
}
Plymouth.SetBootProgressFunction(progress_callback);
// ============================================================
// DIALOG: ЗАПРОС ПАРОЛЯ
// ============================================================
status = "normal";
box_form.image = Image("BOX.png");
box_form.image = box_form.image.Scale(
Percent(80, side_panel.width),
Percent(20, side_panel.height)
);
box_form.width = box_form.image.GetWidth();
box_form.height = box_form.image.GetHeight();
box_form.x = Percent(50, side_panel.width) - Percent(50, box_form.width);
box_form.y = progress_form.y + progress_form.height + Percent(aspect_ratio, side_panel.height);
box_form.z = 10000;
box_form.sprite = Sprite(box_form.image);
box_form.sprite.SetPosition(box_form.x, box_form.y, box_form.z);
box_form.sprite.SetOpacity(0);
fun dialog_setup() {
local.box_form = global.box_form;
local.lock;
local.entry;
local.hint;
local.bullet_i;
box_form.sprite.SetOpacity(1);
entry.image = Image("ENTRY.png");
entry.image = entry.image.Scale(
Percent(90, box_form.width),
Percent(20, box_form.height)
);
entry.width = entry.image.GetWidth();
entry.height = entry.image.GetHeight();
entry.x = box_form.x + box_form.width / 2 - entry.width / 2;
entry.y = box_form.y + (box_form.height / 3 * 2) - entry.height / 2;
entry.z = box_form.z + 1;
entry.sprite = Sprite(entry.image);
entry.sprite.SetPosition(entry.x, entry.y, entry.z);
lock.image = Image("LOCK.png");
lock.image = lock.image.Scale(
Percent(95, entry.height),
Percent(95, entry.height)
);
lock.width = lock.image.GetWidth();
lock.height = lock.image.GetHeight();
lock.x = entry.x + entry.width - lock.width - (entry.height - lock.height);
lock.y = entry.y + entry.height / 2 - lock.height / 2;
lock.z = box_form.z + 2;
lock.sprite = Sprite(lock.image);
lock.sprite.SetPosition(lock.x, lock.y, lock.z);
hint.sprite = Sprite();
hint.x = entry.x;
hint.y = box_form.y + Percent(5, box_form.height);
hint.z = box_form.z + 1;
hint.sprite.SetPosition(hint.x, hint.y, hint.z);
bullet_i.image = Image("BULLET.png");
bullet_i.image = bullet_i.image.Scale(
Percent(50, entry.height),
Percent(50, entry.height)
);
bullet_i.width = bullet_i.image.GetWidth();
bullet_i.height = bullet_i.image.GetHeight();
global.dialog.box_form = box_form;
global.dialog.lock = lock;
global.dialog.entry = entry;
global.dialog.hint = hint;
global.dialog.bullet_i = bullet_i;
dialog_opacity(1);
}
fun dialog_opacity(opacity) {
if (opacity == 0) {
dialog.box_form.sprite.SetOpacity(opacity);
dialog.lock.sprite.SetOpacity(opacity);
dialog.entry.sprite.SetOpacity(opacity);
} else {
box_form.sprite.SetOpacity(opacity);
dialog.lock.sprite.SetOpacity(opacity);
dialog.entry.sprite.SetOpacity(opacity);
}
dialog.hint.sprite.SetOpacity(opacity);
local.index;
for (index = 0; dialog.bullet[index]; index++) {
dialog.bullet[index].sprite.SetOpacity(opacity);
}
}
// ============================================================
// Изменение позиции интерфейса исходя из его общей высоты
// ============================================================
side_block.height = side_block.height + Percent(aspect_ratio, side_panel.height) + box_form.height;
yyy = (side_panel.height - side_block.height) / 2;
messages_box.y = messages_box.y + yyy;
progress_form.y = progress_form.y + yyy;
spinner.y = spinner.y + yyy;
progress_text.y = progress_text.y + yyy;
progress_box.y = progress_box.y + yyy;
progress_bar.y = progress_bar.y + yyy;
box_form.y = box_form.y + yyy;
messages_box.sprite.SetPosition(messages_box.x, messages_box.y, 12);
progress_form.sprite.SetPosition(progress_form.x, progress_form.y, 12);
spinner.sprite.SetPosition(spinner.x, spinner.y, 13);
progress_text.sprite.SetPosition(progress_text.x, progress_text.y, 13);
progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 13);
progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 14);
box_form.sprite.SetPosition(box_form.x, box_form.y, box_form.z);
// ============================================================
fun display_normal_callback() {
global.status = "normal";
if (global.dialog)
dialog_opacity(0);
}
Plymouth.SetDisplayNormalFunction(display_normal_callback);
fun display_password_callback(prompt_text, bullets) {
global.status = "password";
if (!global.dialog)
dialog_setup();
else
dialog_opacity(1);
local.padding_top_bottom = Percent(10, dialog.box_form.height);
local.padding_left_right = Percent(5, dialog.box_form.width);
local.LINE_WIDTH = 100;
while ((LINE_WIDTH * common_symbol.width) > (dialog.box_form.width - (padding_left_right * 2))) {
LINE_WIDTH = LINE_WIDTH - 1;
}
local.str_len = StringLength(prompt_text);
local.line_count = 0;
local.i;
for (i = 1; LINE_WIDTH * i <= str_len; i++) {
line_count = i;
}
if (str_len > LINE_WIDTH) {
if (str_len % LINE_WIDTH > 0) {
line_count = line_count + 1;
}
}
i = 1;
local.start = 0;
local.end = LINE_WIDTH;
local.text = "";
for (i = 1; i <= line_count; i++) {
text += prompt_text.SubString(start, end);
text += "\n";
start = end;
if (LINE_WIDTH * i < str_len) {
end = end + LINE_WIDTH;
} else {
end = str_len;
}
}
prompt_text = text;
dialog.hint.image = Image.Text(prompt_text, 0.5, 0.5, 0.5, 1, common_font);
dialog.hint.sprite.SetImage(dialog.hint.image);
// Важно: обновляем позицию после смены изображения
dialog.hint.sprite.SetPosition(dialog.hint.x, dialog.hint.y, dialog.hint.z);
local.index;
for (index = 0; dialog.bullet[index] || index < bullets; index++) {
if (!dialog.bullet[index]) {
dialog.bullet[index].sprite = Sprite(dialog.bullet_i.image);
dialog.bullet[index].x = dialog.entry.x + index * dialog.bullet_i.width / 0.9 - dialog.entry.height * -0.2;
dialog.bullet[index].y = dialog.entry.y + (dialog.entry.height - dialog.bullet_i.height) / 2;
dialog.bullet[index].z = dialog.entry.z + 1;
dialog.bullet[index].sprite.SetPosition(
dialog.bullet[index].x,
dialog.bullet[index].y,
dialog.bullet[index].z
);
}
if (index < bullets)
dialog.bullet[index].sprite.SetOpacity(1);
else
dialog.bullet[index].sprite.SetOpacity(0);
}
}
Plymouth.SetDisplayPasswordFunction(display_password_callback);
// ============================================================
// ГЛАВНЫЙ ЛОГОТИП
// ============================================================
logo.image = Image("MAIN_LOGO.png");
logo.image = FitIntoDimensions(logo.image, Limit(screen.width, screen.height, 15));
logo.width = logo.image.GetWidth();
logo.height = logo.image.GetHeight();
logo.sprite = Sprite(logo.image);
logo.x = screen.width - logo.image.GetWidth() - messages_box.x;
logo.y = yyy;
logo.sprite.SetOpacity(1);
logo.sprite.SetPosition(logo.x, logo.y, 12);
// ============================================================
// ССЫЛКА НА ПРОЕКТ
// ============================================================
project_link.image = Image.Text("https://Melawy.ru", 0.5, 0.5, 0.5, 1, link_font);
project_link.width = project_link.image.GetWidth();
project_link.height = project_link.image.GetHeight();
project_link.sprite = Sprite(project_link.image);
project_link.x = logo.x + logo.width / 2 - project_link.width / 2;
project_link.y = logo.y + logo.height + Percent(aspect_ratio, screen.height);
project_link.sprite.SetOpacity(1);
project_link.sprite.SetPosition(project_link.x, project_link.y, 15);
// ============================================================
// REFRESH: ОСНОВНОЙ ЦИКЛ АНИМАЦИИ
// ============================================================
time = 1;
// FSCK переменные (для совместимости с Plymouth API)
fsck_running = 0;
fsck_done_fading = 0;
fsck_fade_in_counter = 0;
fsck_progress_meter_sprite = Sprite();
fsck_progress_fade_sprite = Sprite();
fun refreshHandler() {
// Показываем/скрываем элементы в зависимости от режима
if (global.status == "normal" && Plymouth.GetMode() == "boot") {
progress_fade.sprite.SetOpacity(0);
progress_bar.sprite.SetOpacity(1);
local.text_img = Image.Text(starting_text, 0.5, 0.5, 0.5, 1, common_font);
local.text_spr = Sprite(text_img);
text_spr.SetPosition(
progress_box.x,
progress_box.y - progress_text.image.GetHeight() * 2,
13
);
} else {
progress_fade.sprite.SetOpacity(1);
progress_bar.sprite.SetOpacity(0);
local.text_end_img = Image.Text(bye_text, 0.5, 0.5, 0.5, 1, common_font);
local.text_end_spr = Sprite(text_end_img);
text_end_spr.SetPosition(
progress_box.x,
progress_box.y - progress_text.image.GetHeight() * 2,
13
);
}
// Анимация fade-полоски
progress_fade.sprite.SetPosition(
counter + progress_bar.x,
progress_bar.y,
14
);
if (fade_dir == 0) {
counter++;
if (counter >= max_counter) {
fade_dir = 1;
}
} else {
counter--;
if (counter <= 0) {
fade_dir = 0;
}
}
// Скрываем спиннер при fsck или запросе пароля
if (fsck_running == 1 || (global.dialog && dialog.hint.sprite.GetOpacity() == 1)) {
spinner.sprite.SetOpacity(0);
} else {
spinner.sprite.SetOpacity(1);
time++;
local.theta = time / (Math.Pi * 2);
spinner.sprite.SetImage(spinner.image.Rotate(theta));
}
// FSCK анимация плавного появления
if ((fsck_running == 1) && (fsck_done_fading == 0)) {
fsck_progress_meter_sprite.SetOpacity(fsck_fade_in_counter);
fsck_progress_fade_sprite.SetOpacity(fsck_fade_in_counter);
if (fsck_fade_in_counter < 1) {
fsck_fade_in_counter += 0.025;
} else {
fsck_done_fading = 1;
}
}
}
Plymouth.SetRefreshFunction(refreshHandler);
// ============================================================
// CALLBACK: ОДИНОЧНОЕ СООБЩЕНИЕ
// ============================================================
message_status.sprite = Sprite();
fun message_callback(prompt_text) {
message_status.image = Image.Text(prompt_text, 0.5, 0.5, 0.5, 1, common_font);
message_status.sprite.SetImage(message_status.image);
message_status.height = message_status.image.GetHeight();
message_status.x = messages_box.x + Percent(5, messages_box.width);
message_status.y = messages_box.y + messages_box.height - Percent(7, messages_box.height);
message_status.sprite.SetOpacity(1);
message_status.sprite.SetPosition(message_status.x, message_status.y, 13);
}
Plymouth.SetMessageFunction(message_callback);
// ============================================================
// СКРОЛЛЯЩИЙСЯ ЛОГ
// ============================================================
local.padding_top_bottom = Percent(10, messages_box.height);
local.padding_left_right = Percent(5, messages_box.width);
message_sprite_.x = messages_box.x + padding_left_right;
message_sprite_.y = messages_box.y + padding_top_bottom;
NUM_SCROLL_LINES = 100;
LINE_WIDTH_LOG = 100;
LINE_HEIGHT = common_symbol.height * 1.5;
// Подстраиваем количество строк под высоту блока сообщений
local.index;
for (index = NUM_SCROLL_LINES; index > 0; index--) {
if ((NUM_SCROLL_LINES * LINE_HEIGHT) > (messages_box.height - (padding_top_bottom * 2))) {
NUM_SCROLL_LINES = NUM_SCROLL_LINES - 1;
} else {
break;
}
}
// Подстраиваем ширину строки под ширину блока сообщений
for (index = LINE_WIDTH_LOG; index > 0; index--) {
if ((LINE_WIDTH_LOG * common_symbol.width) > (messages_box.width - (padding_left_right * 2))) {
LINE_WIDTH_LOG = LINE_WIDTH_LOG - 1;
} else {
break;
}
}
// Инициализация спрайтов для каждой строки лога
local.i;
for (i = 0; i < NUM_SCROLL_LINES; i++) {
lines[i] = Image.Text("", 0.5, 0.5, 0.5, 1, common_font);
message_sprite[i] = Sprite();
message_sprite[i].SetPosition(
message_sprite_.x,
message_sprite_.y + (i * LINE_HEIGHT),
13
);
}
fun StringLength(string) {
local.index = 0;
local.str = String(string);
while (str.CharAt(index)) index++;
return index;
}
fun scroll_message_callback(text) {
// Обрезаем слишком длинные сообщения
if (StringLength(text) > LINE_WIDTH_LOG) {
text = text.SubString(0, LINE_WIDTH_LOG - 1);
text += "…";
}
// Сдвигаем все сообщения вверх на одну строку
local.i;
for (i = 0; i < NUM_SCROLL_LINES - 1; i++) {
lines[i] = lines[i + 1];
}
// Создаём изображение для последнего сообщения
lines[i] = Image.Text(text, 0.5, 0.5, 0.5, 1, common_font);
// Обновляем все спрайты
for (i = 0; i < NUM_SCROLL_LINES; i++) {
message_sprite[i].SetImage(lines[i]);
}
}
Plymouth.SetUpdateStatusFunction(scroll_message_callback);
// ============================================================
// CALLBACK: ЗАВЕРШЕНИЕ
// ============================================================
fun quit_callback() {
progress_fade.sprite.SetOpacity(1);
progress_bar.sprite.SetOpacity(0);
if (Plymouth.GetMode() == "shutdown") {
logo.sprite.SetOpacity(0);
}
}
Plymouth.SetQuitFunction(quit_callback);