Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
bcd2d83
Enable custom event loop support
SparkyTD Mar 4, 2026
fd52939
NameOfEvent::is_pressed() now also matches TouchStart to correctly re…
SparkyTD Mar 4, 2026
4b388d5
Add is_touch_released() to SourceEvent trait
SparkyTD Mar 4, 2026
59010e4
Add support for global touch event handlers
SparkyTD Mar 4, 2026
5ce6481
Recreate window and rendering surface when the app is resumed after b…
SparkyTD Mar 4, 2026
29a9295
Recreate window and rendering surface when the app is resumed after b…
SparkyTD Mar 4, 2026
8b3670d
Remove redundant closure
SparkyTD Mar 5, 2026
75df545
Remove redundant copy
SparkyTD Mar 5, 2026
f08fccf
Revert changes in event handlers
SparkyTD Mar 5, 2026
27ff7aa
Fix Android is_pressed handling
SparkyTD Mar 6, 2026
2431de2
Fix ScrollView for Android
SparkyTD Mar 6, 2026
93bf654
Format code with 'just f'
SparkyTD Mar 7, 2026
87cbe12
Add Android scrolling to VirtualScrollView
SparkyTD Mar 7, 2026
51ba8b5
Update crates/freya-winit/src/window.rs
SparkyTD Mar 7, 2026
be138b5
Add comment explaining the Android Resume logic
SparkyTD Mar 7, 2026
86a8f60
Rename AppWindow::attributes to window_attributes
SparkyTD Mar 7, 2026
00aa5dc
Add tests for EventName::TouchReleased in ragnarok
SparkyTD Mar 8, 2026
674f7b6
Rename incorrect window_attributes field reference to attributes
SparkyTD Mar 8, 2026
19491ef
Rename incorrect attributes field reference to window_attributes
SparkyTD Mar 8, 2026
f519031
Formatting
SparkyTD Mar 8, 2026
66085f2
Added Android example project
SparkyTD Mar 6, 2026
78df55f
Add README.md to the Android example
SparkyTD Mar 6, 2026
e0dbfe4
Add experimental warning to README.md
SparkyTD Mar 6, 2026
cc255cc
Change default windowSoftInputMode to adjustResize
SparkyTD Mar 6, 2026
655ac9c
Remove explicit android-activity dependency from Cargo.toml
SparkyTD Mar 6, 2026
714ed59
Include CLI Android build instructions in README.md
SparkyTD Mar 7, 2026
4402f97
Remove some unused code from the android example
SparkyTD Mar 8, 2026
061d0f9
Formatting
SparkyTD Mar 8, 2026
e5bf4c2
Separated desktop and android specific entry points in the new exampl…
SparkyTD Mar 8, 2026
9baf02f
Add new CI action for testing NDK build (Android)
SparkyTD Mar 8, 2026
3a27f9d
Use NDK version r26d for CI build
SparkyTD Mar 8, 2026
2b8b792
Add tests to ScrollView
SparkyTD Mar 8, 2026
68745c9
Replace unwrap() call with an error log to prevent hard-crash during …
SparkyTD Mar 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/rust_android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Rust Android Build

on:
workflow_dispatch:
push:
branches: ["main"]
pull_request:

env:
CARGO_TERM_COLOR: always
ANDROID_NDK_HOME: /home/runner/android-ndk-r26d
ANDROID_NDK: /home/runner/android-ndk-r26d

jobs:
android:
runs-on: ubuntu-latest
concurrency:
group: rust-android-${{ github.ref_name }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 2
submodules: true
- name: Remove unused programs
run: |
sudo rm -rf /usr/share/dotnet /opt/ghc /opt/hostedtoolcache/CodeQL
- name: Install NDK r26d
run: |
curl -sSfL -o ndk.zip https://dl.google.com/android/repository/android-ndk-r26d-linux.zip
unzip -q ndk.zip -d /home/runner
rm ndk.zip
- name: Install stable rust toolchain
uses: dtolnay/rust-toolchain@1.90
with:
targets: aarch64-linux-android
- name: Install cargo-ndk
run: cargo install cargo-ndk
- name: Set up cargo cache
uses: Swatinem/rust-cache@v2
if: github.ref != 'refs/heads/main'
with:
shared-key: "rust-android"
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Build Android example
run: cargo ndk -t arm64-v8a build -p android --lib
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ members = [
"./crates/freya-code-editor",
"./examples/ai-chat",
"./examples/state_query_sqlite",
"./examples/android",
]

[workspace.dependencies]
Expand Down
40 changes: 40 additions & 0 deletions crates/freya-components/src/scrollviews/scrollview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub struct ScrollView {
scroll_with_arrows: bool,
scroll_controller: Option<ScrollController>,
invert_scroll_wheel: bool,
drag_scrolling: bool,
key: DiffKey,
}

Expand Down Expand Up @@ -94,6 +95,7 @@ impl Default for ScrollView {
scroll_with_arrows: true,
scroll_controller: None,
invert_scroll_wheel: false,
drag_scrolling: cfg!(target_os = "android"),
key: DiffKey::None,
}
}
Expand Down Expand Up @@ -136,6 +138,11 @@ impl ScrollView {
self
}

pub fn drag_scrolling(mut self, drag_scrolling: bool) -> Self {
self.drag_scrolling = drag_scrolling;
self
}

pub fn max_width(mut self, max_width: impl Into<Size>) -> Self {
self.layout.maximum_width = max_width.into();
self
Expand Down Expand Up @@ -166,9 +173,11 @@ impl Component for ScrollView {
let mut scroll_controller = self
.scroll_controller
.unwrap_or_else(|| use_scroll_controller(ScrollConfig::default));
let mut dragging_content = use_state::<Option<(f64, f64)>>(|| None);
let (scrolled_x, scrolled_y) = scroll_controller.into();
let layout = &self.layout.layout;
let direction = layout.direction;
let drag_scrolling = self.drag_scrolling;

scroll_controller.use_apply(
size.read().inner_sizes.width,
Expand Down Expand Up @@ -221,6 +230,10 @@ impl Component for ScrollView {
e.prevent_default();
clicking_scrollbar.set(None);
}

if drag_scrolling && dragging_content.read().is_some() {
dragging_content.set(None);
}
};

let on_wheel = move |e: Event<WheelEventData>| {
Expand Down Expand Up @@ -264,6 +277,23 @@ impl Component for ScrollView {
};

let on_capture_global_pointer_move = move |e: Event<PointerEventData>| {
if drag_scrolling {
let current_drag = *dragging_content.peek();
if let Some((prev_x, prev_y)) = current_drag {
let coords = e.global_location();
let delta_x = (prev_x - coords.x) as f32;
let delta_y = (prev_y - coords.y) as f32;

scroll_controller.scroll_to_y((corrected_scrolled_y - delta_y) as i32);
scroll_controller.scroll_to_x((corrected_scrolled_x - delta_x) as i32);

dragging_content.set(Some((coords.x, coords.y)));
e.prevent_default();
timeout.reset();
return;
}
}

let clicking_scrollbar = clicking_scrollbar.peek();

if let Some((Axis::Y, y)) = *clicking_scrollbar {
Expand Down Expand Up @@ -348,6 +378,15 @@ impl Component for ScrollView {
}
};

let on_pointer_down = move |e: Event<PointerEventData>| {
if drag_scrolling {
let coords = e.global_location();
dragging_content.set(Some((coords.x, coords.y)));
focus.request_focus();
timeout.reset();
}
};

rect()
.width(layout.width.clone())
.height(layout.height.clone())
Expand All @@ -368,6 +407,7 @@ impl Component for ScrollView {
.on_key_down(on_key_down)
.on_global_key_up(on_global_key_up)
.on_global_key_down(on_global_key_down)
.on_pointer_down(on_pointer_down)
.child(
rect()
.width(container_width.clone())
Expand Down
43 changes: 43 additions & 0 deletions crates/freya-components/src/scrollviews/virtual_scrollview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub struct VirtualScrollView<D, B: Fn(usize, &D) -> Element> {
scroll_with_arrows: bool,
scroll_controller: Option<ScrollController>,
invert_scroll_wheel: bool,
drag_scrolling: bool,
key: DiffKey,
}

Expand Down Expand Up @@ -123,6 +124,7 @@ impl<B: Fn(usize, &()) -> Element> VirtualScrollView<(), B> {
scroll_with_arrows: true,
scroll_controller: None,
invert_scroll_wheel: false,
drag_scrolling: cfg!(target_os = "android"),
key: DiffKey::None,
}
}
Expand All @@ -143,6 +145,7 @@ impl<B: Fn(usize, &()) -> Element> VirtualScrollView<(), B> {
scroll_with_arrows: true,
scroll_controller: Some(scroll_controller),
invert_scroll_wheel: false,
drag_scrolling: cfg!(target_os = "android"),
key: DiffKey::None,
}
}
Expand All @@ -165,6 +168,7 @@ impl<D, B: Fn(usize, &D) -> Element> VirtualScrollView<D, B> {
scroll_with_arrows: true,
scroll_controller: None,
invert_scroll_wheel: false,
drag_scrolling: cfg!(target_os = "android"),
key: DiffKey::None,
}
}
Expand All @@ -190,6 +194,7 @@ impl<D, B: Fn(usize, &D) -> Element> VirtualScrollView<D, B> {
scroll_with_arrows: true,
scroll_controller: Some(scroll_controller),
invert_scroll_wheel: false,
drag_scrolling: cfg!(target_os = "android"),
key: DiffKey::None,
}
}
Expand Down Expand Up @@ -224,6 +229,11 @@ impl<D, B: Fn(usize, &D) -> Element> VirtualScrollView<D, B> {
self
}

pub fn drag_scrolling(mut self, drag_scrolling: bool) -> Self {
self.drag_scrolling = drag_scrolling;
self
}

pub fn scroll_controller(
mut self,
scroll_controller: impl Into<Option<ScrollController>>,
Expand Down Expand Up @@ -256,9 +266,11 @@ impl<D: PartialEq + 'static, B: Fn(usize, &D) -> Element + 'static> Component
let mut scroll_controller = self
.scroll_controller
.unwrap_or_else(|| use_scroll_controller(ScrollConfig::default));
let mut dragging_content = use_state::<Option<(f64, f64)>>(|| None);
let (scrolled_x, scrolled_y) = scroll_controller.into();
let layout = &self.layout.layout;
let direction = layout.direction;
let drag_scrolling = self.drag_scrolling;

let (inner_width, inner_height) = match direction {
Direction::Vertical => (
Expand Down Expand Up @@ -305,6 +317,10 @@ impl<D: PartialEq + 'static, B: Fn(usize, &D) -> Element + 'static> Component
e.prevent_default();
clicking_scrollbar.set(None);
}

if drag_scrolling && dragging_content.read().is_some() {
dragging_content.set(None);
}
};

let on_wheel = move |e: Event<WheelEventData>| {
Expand Down Expand Up @@ -348,6 +364,23 @@ impl<D: PartialEq + 'static, B: Fn(usize, &D) -> Element + 'static> Component
};

let on_capture_global_pointer_move = move |e: Event<PointerEventData>| {
if drag_scrolling {
let current_drag = *dragging_content.peek();
if let Some((prev_x, prev_y)) = current_drag {
let coords = e.global_location();
let delta_x = (prev_x - coords.x) as f32;
let delta_y = (prev_y - coords.y) as f32;

scroll_controller.scroll_to_y((corrected_scrolled_y - delta_y) as i32);
scroll_controller.scroll_to_x((corrected_scrolled_x - delta_x) as i32);

dragging_content.set(Some((coords.x, coords.y)));
e.prevent_default();
timeout.reset();
return;
}
}

let clicking_scrollbar = clicking_scrollbar.peek();

if let Some((Axis::Y, y)) = *clicking_scrollbar {
Expand Down Expand Up @@ -467,6 +500,15 @@ impl<D: PartialEq + 'static, B: Fn(usize, &D) -> Element + 'static> Component
}
};

let on_pointer_down = move |e: Event<PointerEventData>| {
if drag_scrolling {
let coords = e.global_location();
dragging_content.set(Some((coords.x, coords.y)));
focus.request_focus();
timeout.reset();
}
};

rect()
.width(layout.width.clone())
.height(layout.height.clone())
Expand All @@ -485,6 +527,7 @@ impl<D: PartialEq + 'static, B: Fn(usize, &D) -> Element + 'static> Component
.on_key_down(on_key_down)
.on_global_key_up(on_global_key_up)
.on_global_key_down(on_global_key_down)
.on_pointer_down(on_pointer_down)
.child(
rect()
.width(container_width.clone())
Expand Down
Loading
Loading