android.rs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use crate::{
  19. android,
  20. gfx::Point,
  21. mesh::Color,
  22. prop::{PropertyColor, PropertyFloat32},
  23. text2::{TextContext, TEXT_CTX},
  24. };
  25. macro_rules! t { ($($arg:tt)*) => { trace!(target: "text::editor::android", $($arg)*); } }
  26. // You must be careful working with string indexes in Java. They are UTF16 string indexs, not UTF8
  27. fn char16_to_byte_index(s: &str, char_idx: usize) -> Option<usize> {
  28. let utf16_data: Vec<_> = s.encode_utf16().take(char_idx).collect();
  29. let prestr = String::from_utf16(&utf16_data).ok()?;
  30. Some(prestr.len())
  31. }
  32. fn byte_to_char16_index(s: &str, byte_idx: usize) -> Option<usize> {
  33. if byte_idx > s.len() || !s.is_char_boundary(byte_idx) {
  34. return None
  35. }
  36. Some(s[..byte_idx].encode_utf16().count())
  37. }
  38. pub struct Editor {
  39. pub composer_id: usize,
  40. is_init: bool,
  41. layout: parley::Layout<Color>,
  42. width: Option<f32>,
  43. font_size: PropertyFloat32,
  44. text_color: PropertyColor,
  45. window_scale: PropertyFloat32,
  46. lineheight: PropertyFloat32,
  47. }
  48. impl Editor {
  49. pub async fn new(
  50. font_size: PropertyFloat32,
  51. text_color: PropertyColor,
  52. window_scale: PropertyFloat32,
  53. lineheight: PropertyFloat32,
  54. ) -> Self {
  55. Self {
  56. composer_id: usize::MAX,
  57. is_init: false,
  58. layout: Default::default(),
  59. width: None,
  60. font_size,
  61. text_color,
  62. window_scale,
  63. lineheight,
  64. }
  65. }
  66. pub fn init(&mut self) {
  67. android::focus(self.composer_id).unwrap();
  68. }
  69. pub fn setup(&mut self) {
  70. assert!(self.composer_id != usize::MAX);
  71. t!("Initialized composer [{}]", self.composer_id);
  72. let atxt = "A berry is small 😊 and pulpy.";
  73. android::set_text(self.composer_id, atxt).unwrap();
  74. self.is_init = true;
  75. }
  76. pub async fn refresh(&mut self) {
  77. let font_size = self.font_size.get();
  78. let text_color = self.text_color.get();
  79. let window_scale = self.window_scale.get();
  80. let lineheight = self.lineheight.get();
  81. let edit = android::get_editable(self.composer_id).unwrap();
  82. t!("refesh buffer = {}", edit.buffer);
  83. let mut txt_ctx = TEXT_CTX.get().await;
  84. self.layout = txt_ctx.make_layout(
  85. &edit.buffer,
  86. text_color,
  87. font_size,
  88. lineheight,
  89. window_scale,
  90. self.width,
  91. );
  92. }
  93. pub fn layout(&self) -> &parley::Layout<Color> {
  94. &self.layout
  95. }
  96. pub fn move_to_pos(&self, pos: Point) {
  97. let cursor = parley::Cursor::from_point(&self.layout, pos.x, pos.y);
  98. let edit = android::get_editable(self.composer_id).unwrap();
  99. let cursor_idx = cursor.index();
  100. let pos = byte_to_char16_index(&edit.buffer, cursor_idx).unwrap();
  101. android::set_selection(self.composer_id, pos, pos);
  102. t!(" {cursor_idx} => {pos}");
  103. }
  104. pub fn get_cursor_pos(&self) -> Option<Point> {
  105. if !self.is_init {
  106. return None
  107. }
  108. let lineheight = self.lineheight.get();
  109. let edit = android::get_editable(self.composer_id).unwrap();
  110. let cursor_byte_idx = char16_to_byte_index(&edit.buffer, edit.select_start).unwrap();
  111. let cursor = if cursor_byte_idx >= edit.buffer.len() {
  112. parley::Cursor::from_byte_index(
  113. &self.layout,
  114. edit.buffer.len(),
  115. parley::Affinity::Upstream,
  116. )
  117. } else {
  118. parley::Cursor::from_byte_index(
  119. &self.layout,
  120. cursor_byte_idx,
  121. parley::Affinity::Downstream,
  122. )
  123. };
  124. let cursor_rect = cursor.geometry(&self.layout, lineheight);
  125. let cursor_pos = Point::new(cursor_rect.x0 as f32, cursor_rect.y0 as f32);
  126. Some(cursor_pos)
  127. }
  128. pub async fn driver<'a>(
  129. &'a mut self,
  130. txt_ctx: &'a mut TextContext,
  131. ) -> Option<parley::PlainEditorDriver<'a, Color>> {
  132. None
  133. }
  134. pub fn set_width(&mut self, w: f32) {
  135. self.width = Some(w);
  136. }
  137. pub fn height(&self) -> f32 {
  138. self.layout().height()
  139. }
  140. }