Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7f181f1d9 | ||
|
|
6e1aee94b7 | ||
|
|
2684a3f7bc |
@@ -4,6 +4,9 @@ Yume Nikki desktop pet ported for Wayland KDE
|
||||
|
||||

|
||||
|
||||
## Credits
|
||||
[Sierpinski Simon, Yume Nikki Desktop Pet for Windows](https://sierpinski-simon.itch.io/yume-nikki-desktop-pet)
|
||||
|
||||
## Install
|
||||
|
||||
1. Download `yume4kde.flatpak` from the
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"install -Dm644 packaging/jp.angeltech.yume4kde.png /app/share/icons/hicolor/32x32/apps/jp.angeltech.yume4kde.png"
|
||||
],
|
||||
"sources": [
|
||||
{"type": "archive", "url": "https://git.acid.im/Angel-Technologies/yume4kde/archive/v0.0.2.tar.gz", "sha256": "97809c3ee97e7d8f0ca83b8758a2761802ed6df3c32ecad15e38490bfc509a5b"},
|
||||
{"type": "archive", "url": "https://git.acid.im/Angel-Technologies/yume4kde/archive/v0.0.3.tar.gz", "sha256": "65e43e2c0ea638b39d0a4fca37edaa0086eca5c37474936fbade0ab8642131a0"},
|
||||
"packaging/cargo-sources.json"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
#include <QFile>
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
#include <QSettings>
|
||||
#include <QtQml/qqml.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
constexpr auto scriptName = "jp.angeltech.yume4kde.WindowSurfaces";
|
||||
@@ -31,6 +33,31 @@ void yume4kde::prepare_application()
|
||||
qputenv("QT_QUICK_CONTROLS_STYLE", "Basic");
|
||||
}
|
||||
|
||||
double yume4kde::load_size_factor()
|
||||
{
|
||||
QSettings settings(QSettings::IniFormat, QSettings::UserScope,
|
||||
QStringLiteral("jp.angeltech"), QStringLiteral("yume4kde"));
|
||||
bool ok = false;
|
||||
const double factor = settings.value(QStringLiteral("size_factor"), 1.0).toDouble(&ok);
|
||||
if (!ok || !std::isfinite(factor))
|
||||
return 1.0;
|
||||
return factor;
|
||||
}
|
||||
|
||||
void yume4kde::save_size_factor(double factor)
|
||||
{
|
||||
if (!std::isfinite(factor)) {
|
||||
qWarning() << "Cannot save non-finite size factor.";
|
||||
return;
|
||||
}
|
||||
QSettings settings(QSettings::IniFormat, QSettings::UserScope,
|
||||
QStringLiteral("jp.angeltech"), QStringLiteral("yume4kde"));
|
||||
settings.setValue(QStringLiteral("size_factor"), factor);
|
||||
settings.sync();
|
||||
if (settings.status() != QSettings::NoError)
|
||||
qWarning() << "Failed to save size factor:" << settings.status();
|
||||
}
|
||||
|
||||
void yume4kde::register_platform_types()
|
||||
{
|
||||
QGuiApplication::setDesktopFileName(applicationService);
|
||||
|
||||
@@ -16,6 +16,8 @@ class QScreen;
|
||||
namespace yume4kde {
|
||||
void prepare_application();
|
||||
void register_platform_types();
|
||||
double load_size_factor();
|
||||
void save_size_factor(double factor);
|
||||
}
|
||||
|
||||
class DesktopIntegration : public QObject
|
||||
|
||||
+5
-5
@@ -14,6 +14,7 @@ Window {
|
||||
height: desktop.screenHeight
|
||||
|
||||
property int selectedActorId: -1
|
||||
property real wheelRemainder: 0
|
||||
|
||||
DesktopIntegration {
|
||||
id: desktop
|
||||
@@ -117,7 +118,6 @@ Window {
|
||||
property bool dragged: false
|
||||
property point pressPosition
|
||||
property point pressOffset
|
||||
property real wheelRemainder: 0
|
||||
onPressed: function(mouse) {
|
||||
const p = mapToItem(root.contentItem, mouse.x, mouse.y)
|
||||
if (!desktop.hitTest(actorDelegate.modelData.imageSource, mouse.x, mouse.y, width, height, actorDelegate.modelData.mirrored)) {
|
||||
@@ -172,11 +172,11 @@ Window {
|
||||
wheel.accepted = false
|
||||
return
|
||||
}
|
||||
wheelRemainder += wheel.angleDelta.y
|
||||
const steps = Math.trunc(wheelRemainder / 120)
|
||||
root.wheelRemainder += wheel.angleDelta.y
|
||||
const steps = Math.trunc(root.wheelRemainder / 120)
|
||||
if (steps !== 0) {
|
||||
wheelRemainder -= steps * 120
|
||||
root.backend.resizeActor(actorDelegate.actorId, steps)
|
||||
root.wheelRemainder -= steps * 120
|
||||
root.backend.resizeCharacters(steps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -306,10 +306,12 @@ impl Backend {
|
||||
self.world.end_drag(id);
|
||||
self.synchronize();
|
||||
}
|
||||
#[qslot(qml_name = "resizeActor")]
|
||||
fn resize_actor(&mut self, id: i32, steps: f64) {
|
||||
self.world.resize_pet(id, steps);
|
||||
self.synchronize();
|
||||
#[qslot(qml_name = "resizeCharacters")]
|
||||
fn resize_characters(&mut self, steps: f64) {
|
||||
if self.world.resize_pets(steps) {
|
||||
crate::platform::save_size_factor(self.world.size_factor());
|
||||
self.synchronize();
|
||||
}
|
||||
}
|
||||
#[qslot(qml_name = "openMenu")]
|
||||
fn open_menu(&mut self, id: i32) {
|
||||
@@ -361,7 +363,7 @@ impl Backend {
|
||||
actors: Vec::new(),
|
||||
decorations: Vec::new(),
|
||||
visible: false,
|
||||
world: World::default(),
|
||||
world: World::with_size_factor(crate::platform::load_size_factor()),
|
||||
last_advance: Instant::now(),
|
||||
exit_requested,
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ mod platform {
|
||||
include!("native/platform.hpp");
|
||||
fn prepare_application();
|
||||
fn register_platform_types();
|
||||
fn load_size_factor() -> f64;
|
||||
fn save_size_factor(factor: f64);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+69
-23
@@ -214,7 +214,6 @@ pub struct PetState {
|
||||
pub sleeping: bool,
|
||||
pub support: Option<Support>,
|
||||
pub mirrored: bool,
|
||||
requested_scale: f64,
|
||||
direction: Direction,
|
||||
motion: HorizontalMotion,
|
||||
velocity_y: f64,
|
||||
@@ -234,7 +233,6 @@ impl PetState {
|
||||
x,
|
||||
y,
|
||||
scale: SPRITE_SCALE,
|
||||
requested_scale: SPRITE_SCALE,
|
||||
dragging: false,
|
||||
sleeping: false,
|
||||
support: None,
|
||||
@@ -352,6 +350,7 @@ pub struct World {
|
||||
pub sounds: Vec<&'static str>,
|
||||
pub quitting: Option<f64>,
|
||||
screen_scale: f64,
|
||||
requested_scale: f64,
|
||||
rng: StdRng,
|
||||
next_id: i32,
|
||||
menu_actor: Option<i32>,
|
||||
@@ -381,6 +380,7 @@ impl World {
|
||||
sounds: Vec::with_capacity(8),
|
||||
quitting: None,
|
||||
screen_scale: 1.0,
|
||||
requested_scale: SPRITE_SCALE,
|
||||
rng,
|
||||
next_id: 2,
|
||||
menu_actor: None,
|
||||
@@ -389,6 +389,15 @@ impl World {
|
||||
flute_remaining: 0.0,
|
||||
}
|
||||
}
|
||||
pub fn with_size_factor(factor: f64) -> Self {
|
||||
let mut world = Self::with_rng(StdRng::from_os_rng());
|
||||
world.requested_scale = normalize_size_factor(factor) * SPRITE_SCALE;
|
||||
world
|
||||
}
|
||||
|
||||
pub fn size_factor(&self) -> f64 {
|
||||
self.requested_scale / SPRITE_SCALE
|
||||
}
|
||||
|
||||
pub fn update_desktop(&mut self, snapshot: DesktopSnapshot) {
|
||||
let first = self.pets.is_empty();
|
||||
@@ -426,7 +435,13 @@ impl World {
|
||||
self.pets.push(pet);
|
||||
}
|
||||
for pet in &mut self.pets {
|
||||
fit_scale(pet, self.bounds, self.screen_scale, self.sitting);
|
||||
fit_scale(
|
||||
pet,
|
||||
self.bounds,
|
||||
self.screen_scale,
|
||||
self.requested_scale,
|
||||
self.sitting,
|
||||
);
|
||||
reconcile_pet(
|
||||
pet,
|
||||
self.sitting,
|
||||
@@ -516,23 +531,25 @@ impl World {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize_pet(&mut self, id: i32, steps: f64) {
|
||||
if self.quitting.is_some() {
|
||||
return;
|
||||
pub fn resize_pets(&mut self, steps: f64) -> bool {
|
||||
if self.quitting.is_some() || !steps.is_finite() {
|
||||
return false;
|
||||
}
|
||||
let requested_scale =
|
||||
(self.requested_scale + steps * 0.25).clamp(SPRITE_SCALE / 2.0, SPRITE_SCALE * 2.0);
|
||||
if requested_scale == self.requested_scale {
|
||||
return false;
|
||||
}
|
||||
self.requested_scale = requested_scale;
|
||||
self.idle_elapsed = 0.0;
|
||||
if let Some(pet) = self.pets.iter_mut().find(|pet| pet.id == id) {
|
||||
let requested_scale =
|
||||
(pet.requested_scale + steps * 0.25).clamp(SPRITE_SCALE / 2.0, SPRITE_SCALE * 2.0);
|
||||
if requested_scale == pet.requested_scale {
|
||||
return;
|
||||
}
|
||||
let previous_scale = pet.scale;
|
||||
pet.requested_scale = requested_scale;
|
||||
fit_scale(pet, self.bounds, self.screen_scale, self.sitting);
|
||||
if pet.scale == previous_scale {
|
||||
return;
|
||||
}
|
||||
for pet in &mut self.pets {
|
||||
fit_scale(
|
||||
pet,
|
||||
self.bounds,
|
||||
self.screen_scale,
|
||||
self.requested_scale,
|
||||
self.sitting,
|
||||
);
|
||||
reconcile_pet(
|
||||
pet,
|
||||
self.sitting,
|
||||
@@ -543,6 +560,7 @@ impl World {
|
||||
self.bounds,
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn open_menu(&mut self, id: i32) {
|
||||
@@ -592,7 +610,13 @@ impl World {
|
||||
self.bounds.y + self.rng.random_range(0.1..0.7) * self.bounds.height,
|
||||
);
|
||||
self.next_id += 1;
|
||||
fit_scale(&mut pet, self.bounds, self.screen_scale, false);
|
||||
fit_scale(
|
||||
&mut pet,
|
||||
self.bounds,
|
||||
self.screen_scale,
|
||||
self.requested_scale,
|
||||
false,
|
||||
);
|
||||
let (width, height) = pet.size(false, None);
|
||||
(pet.x, pet.y) = constrain_position(self.bounds, &self.panels, pet.x, pet.y, width, height);
|
||||
self.pets.push(pet);
|
||||
@@ -647,9 +671,17 @@ impl World {
|
||||
velocity_y: 0.0,
|
||||
});
|
||||
self.next_id += 1;
|
||||
fit_scale(main, self.bounds, self.screen_scale, self.sitting);
|
||||
}
|
||||
for pet in &mut self.pets {
|
||||
fit_scale(
|
||||
pet,
|
||||
self.bounds,
|
||||
self.screen_scale,
|
||||
self.requested_scale,
|
||||
self.sitting,
|
||||
);
|
||||
reconcile_pet(
|
||||
main,
|
||||
pet,
|
||||
self.sitting,
|
||||
self.quitting,
|
||||
&self.surfaces,
|
||||
@@ -947,12 +979,26 @@ impl World {
|
||||
}
|
||||
}
|
||||
|
||||
fn fit_scale(pet: &mut PetState, bounds: Rectangle, screen_scale: f64, sitting: bool) {
|
||||
fn normalize_size_factor(factor: f64) -> f64 {
|
||||
if factor.is_finite() {
|
||||
factor.clamp(0.5, 2.0)
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
fn fit_scale(
|
||||
pet: &mut PetState,
|
||||
bounds: Rectangle,
|
||||
screen_scale: f64,
|
||||
requested_scale: f64,
|
||||
sitting: bool,
|
||||
) {
|
||||
let previous_scale = pet.scale;
|
||||
let (previous_width, _) = pet.size(sitting, None);
|
||||
let previous_anchor = pet.attachment_height(sitting);
|
||||
let image = pet.image(false, None);
|
||||
pet.scale = (screen_scale * pet.requested_scale)
|
||||
pet.scale = (screen_scale * requested_scale)
|
||||
.min(bounds.width / image.width)
|
||||
.min(bounds.height / image.height)
|
||||
.max(0.01);
|
||||
|
||||
+71
-18
@@ -503,12 +503,14 @@ fn wheel_limits_follow_the_screen_relative_default() {
|
||||
let mut world = World::with_rng(StdRng::seed_from_u64(9));
|
||||
world.update_desktop(desktop);
|
||||
for _ in 0..6 {
|
||||
world.resize_pet(1, -1.0);
|
||||
world.resize_pets(-1.0);
|
||||
}
|
||||
assert_eq!(world.size_factor(), 0.5);
|
||||
assert_eq!(world.pets[0].size(false, None), (24.0, 32.0));
|
||||
for _ in 0..18 {
|
||||
world.resize_pet(1, 1.0);
|
||||
world.resize_pets(1.0);
|
||||
}
|
||||
assert_eq!(world.size_factor(), 2.0);
|
||||
assert_eq!(world.pets[0].size(false, None), (96.0, 128.0));
|
||||
}
|
||||
|
||||
@@ -519,7 +521,7 @@ fn screen_resize_preserves_user_scale_and_drag_grip() {
|
||||
world.pets[0].y = 350.0;
|
||||
let cursor = (318.0, 380.0);
|
||||
world.begin_drag(1, cursor.0, cursor.1);
|
||||
world.resize_pet(1, 6.0);
|
||||
world.resize_pets(6.0);
|
||||
assert_eq!(world.pets[0].size(false, None), (108.0, 144.0));
|
||||
|
||||
let mut desktop = snapshot(Vec::new());
|
||||
@@ -557,17 +559,17 @@ fn screen_resize_keeps_the_pet_on_its_titlebar() {
|
||||
#[test]
|
||||
fn resize_limits_survive_desktop_and_effect_changes() {
|
||||
let mut world = world();
|
||||
world.resize_pet(1, 1000.0);
|
||||
world.resize_pets(1000.0);
|
||||
assert_eq!(world.pets[0].size(false, None), (144.0, 192.0));
|
||||
world.resize_pet(1, 1.0);
|
||||
world.resize_pets(1.0);
|
||||
assert_eq!(world.pets[0].size(false, None), (144.0, 192.0));
|
||||
world.update_desktop(snapshot(Vec::new()));
|
||||
world.set_effect(Effect::Bicycle);
|
||||
world.set_effect(Effect::None);
|
||||
assert_eq!(world.pets[0].size(false, None), (144.0, 192.0));
|
||||
world.resize_pet(1, -1000.0);
|
||||
world.resize_pets(-1000.0);
|
||||
assert_eq!(world.pets[0].size(false, None), (36.0, 48.0));
|
||||
world.resize_pet(1, -1.0);
|
||||
world.resize_pets(-1.0);
|
||||
assert_eq!(world.pets[0].size(false, None), (36.0, 48.0));
|
||||
}
|
||||
|
||||
@@ -582,7 +584,7 @@ fn growing_on_the_floor_keeps_clear_of_panels_and_screen_edges() {
|
||||
pet.x = 300.0;
|
||||
pet.y = 656.0;
|
||||
pet.support = Some(Support::Floor);
|
||||
world.resize_pet(1, 1000.0);
|
||||
world.resize_pets(1000.0);
|
||||
let pet = &world.pets[0];
|
||||
let (width, height) = pet.size(false, None);
|
||||
assert_eq!(pet.y + height, 752.0);
|
||||
@@ -595,12 +597,12 @@ fn growing_on_the_floor_keeps_clear_of_panels_and_screen_edges() {
|
||||
fn resizing_preserves_titlebar_feet_and_seat_anchors() {
|
||||
let mut world = world();
|
||||
attach_to_target(&mut world);
|
||||
world.resize_pet(1, 4.0);
|
||||
world.resize_pets(4.0);
|
||||
let pet = &world.pets[0];
|
||||
assert_eq!(pet.y + pet.attachment_height(false), 320.0);
|
||||
assert!(matches!(&pet.support, Some(Support::Window { id, .. }) if id == "target"));
|
||||
world.toggle_sitting();
|
||||
world.resize_pet(1, 4.0);
|
||||
world.resize_pets(4.0);
|
||||
let pet = &world.pets[0];
|
||||
assert_eq!(pet.y + pet.attachment_height(true), 320.0);
|
||||
assert!(pet.x >= 100.0 && pet.x + pet.size(true, None).0 <= 900.0);
|
||||
@@ -615,7 +617,7 @@ fn resizing_during_a_drag_preserves_the_grab_point() {
|
||||
let mut world = world();
|
||||
let cursor = (world.pets[0].x + 18.0, world.pets[0].y + 30.0);
|
||||
world.begin_drag(1, cursor.0, cursor.1);
|
||||
world.resize_pet(1, 1000.0);
|
||||
world.resize_pets(1000.0);
|
||||
let pet = &world.pets[0];
|
||||
assert_eq!((pet.x + 36.0, pet.y + 60.0), cursor);
|
||||
world.drag_to(1, 600.0, 400.0);
|
||||
@@ -629,7 +631,7 @@ fn a_resized_character_can_finish_climbing() {
|
||||
for _ in 0..30 {
|
||||
world.advance(0.03);
|
||||
}
|
||||
world.resize_pet(1, 1000.0);
|
||||
world.resize_pets(1000.0);
|
||||
assert!(matches!(world.pets[0].support, Some(Support::Climbing(_))));
|
||||
assert_eq!(world.pets[0].x + world.pets[0].size(false, None).0, 220.0);
|
||||
for _ in 0..400 {
|
||||
@@ -651,7 +653,7 @@ fn a_resized_character_can_finish_climbing() {
|
||||
#[test]
|
||||
fn screen_fitting_preserves_the_requested_size() {
|
||||
let mut world = world();
|
||||
world.resize_pet(1, 1000.0);
|
||||
world.resize_pets(1000.0);
|
||||
let mut desktop = snapshot(Vec::new());
|
||||
desktop.work_area = rectangle(40.0, 24.0, 160.0, 96.0);
|
||||
world.update_desktop(desktop);
|
||||
@@ -664,12 +666,63 @@ fn screen_fitting_preserves_the_requested_size() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_character_has_an_independent_size() {
|
||||
fn resizing_updates_existing_characters_and_new_characters_inherit_the_size() {
|
||||
let mut world = world();
|
||||
world.add_npc(Character::Poniko);
|
||||
assert!(world.resize_pets(6.0));
|
||||
assert_eq!(world.pets[0].size(false, None), (108.0, 144.0));
|
||||
assert_eq!(world.pets[1].size(false, None), (108.0, 144.0));
|
||||
world.add_npc(Character::Monoko);
|
||||
assert_eq!(world.pets[2].size(false, None), (108.0, 144.0));
|
||||
assert!(world.resize_pets(-6.0));
|
||||
for pet in &world.pets {
|
||||
assert_eq!(pet.size(false, None), (72.0, 96.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resizing_multiple_characters_preserves_drag_and_titlebar_attachment() {
|
||||
let mut world = world();
|
||||
attach_to_target(&mut world);
|
||||
world.add_npc(Character::Poniko);
|
||||
let npc_id = world.pets[1].id;
|
||||
world.resize_pet(1, 1000.0);
|
||||
world.resize_pet(npc_id, -1000.0);
|
||||
assert_eq!(world.pets[0].size(false, None), (144.0, 192.0));
|
||||
assert_eq!(world.pets[1].size(false, None), (36.0, 48.0));
|
||||
world.pets[1].x = 500.0;
|
||||
world.pets[1].y = 500.0;
|
||||
let cursor = (518.0, 530.0);
|
||||
world.begin_drag(npc_id, cursor.0, cursor.1);
|
||||
world.resize_pets(6.0);
|
||||
let main = &world.pets[0];
|
||||
let npc = &world.pets[1];
|
||||
assert_eq!(main.y + main.attachment_height(false), 320.0);
|
||||
assert!(matches!(&main.support, Some(Support::Window { id, .. }) if id == "target"));
|
||||
assert!(npc.dragging);
|
||||
assert_eq!(
|
||||
(npc.x + npc.drag_offset.0, npc.y + npc.drag_offset.1),
|
||||
cursor
|
||||
);
|
||||
assert_eq!(main.size(false, None), (108.0, 144.0));
|
||||
assert_eq!(npc.size(false, None), (108.0, 144.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restored_size_is_bounded_and_invalid_values_use_the_default() {
|
||||
for (factor, expected_size) in [
|
||||
(f64::NAN, (72.0, 96.0)),
|
||||
(f64::INFINITY, (72.0, 96.0)),
|
||||
(0.0, (36.0, 48.0)),
|
||||
(3.0, (144.0, 192.0)),
|
||||
(1.5, (108.0, 144.0)),
|
||||
] {
|
||||
let mut world = World::with_size_factor(factor);
|
||||
world.update_desktop(snapshot(Vec::new()));
|
||||
assert_eq!(world.pets[0].size(false, None), expected_size);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quitting_blocks_size_changes() {
|
||||
let mut world = world();
|
||||
world.start_quit();
|
||||
assert!(!world.resize_pets(1.0));
|
||||
assert_eq!(world.pets[0].size(false, None), (72.0, 96.0));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user