Skip to content

Commit 6400887

Browse files
vrdonsSytten
andauthored
add click_with API for custom click behavior (#285)
* add click_with API for custom click behavior * Add click_with to page handler * Add documentation for ClickOptions * Remove code duplicates --------- Co-authored-by: Emile Fugulin <code@efugulin.com>
1 parent 8d44724 commit 6400887

File tree

5 files changed

+155
-4
lines changed

5 files changed

+155
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
### Added
1515

1616
- Add `add_init_script` to `Page` for scripts before navigation
17+
- Add `click_with` to `Page` and `Element` for custom click behavior
1718

1819
## [0.8.0] 2025-11-28
1920

chromiumoxide_types/src/lib.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,70 @@ impl From<String> for Binary {
266266
Self(expr)
267267
}
268268
}
269+
270+
/// Options that control how a click action is performed.
271+
#[derive(Clone, Debug)]
272+
pub struct ClickOptions {
273+
/// Number of times the click action should be executed.
274+
///
275+
/// A value of `1` represents a single click.
276+
pub click_count: i64,
277+
}
278+
279+
impl Default for ClickOptions {
280+
/// Creates a [`ClickOptions`] instance with default values.
281+
///
282+
/// Default values:
283+
/// - `click_count`: `1`
284+
fn default() -> Self {
285+
Self { click_count: 1 }
286+
}
287+
}
288+
289+
impl ClickOptions {
290+
/// Creates a new [`ClickOptions`] using default values.
291+
pub fn new() -> Self {
292+
Self::default()
293+
}
294+
295+
/// Creates a builder for constructing [`ClickOptions`].
296+
pub fn builder() -> ClickOptionsBuilder {
297+
ClickOptionsBuilder::default()
298+
}
299+
}
300+
301+
/// Builder for [`ClickOptions`].
302+
#[derive(Clone, Debug)]
303+
pub struct ClickOptionsBuilder {
304+
click_count: i64,
305+
}
306+
307+
impl Default for ClickOptionsBuilder {
308+
/// Creates a [`ClickOptionsBuilder`] with default values.
309+
///
310+
/// Default values:
311+
/// - `click_count`: `1`
312+
fn default() -> Self {
313+
Self { click_count: 1 }
314+
}
315+
}
316+
317+
impl ClickOptionsBuilder {
318+
/// Creates a new [`ClickOptionsBuilder`].
319+
pub fn new() -> Self {
320+
Self::default()
321+
}
322+
323+
/// Sets how many times the click action should be executed.
324+
pub fn click_count(mut self, count: impl Into<i64>) -> Self {
325+
self.click_count = count.into();
326+
self
327+
}
328+
329+
/// Builds the [`ClickOptions`] instance.
330+
pub fn build(self) -> ClickOptions {
331+
ClickOptions {
332+
click_count: self.click_count,
333+
}
334+
}
335+
}

src/element.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::pin::Pin;
44
use std::sync::Arc;
55
use std::task::{Context, Poll};
66

7+
use chromiumoxide_types::ClickOptions;
78
use futures::{future, Future, FutureExt, Stream};
89

910
use chromiumoxide_cdp::cdp::browser_protocol::dom::{
@@ -271,6 +272,19 @@ impl Element {
271272
Ok(self)
272273
}
273274

275+
/// Clicks the element using the provided [`ClickOptions`].
276+
///
277+
/// This behaves the same as [`click()`], but allows customizing
278+
/// click behavior such as click count.
279+
///
280+
/// Note that if the click triggers a navigation, this element
281+
/// may no longer exist afterwards.
282+
pub async fn click_with(&self, options: ClickOptions) -> Result<&Self> {
283+
let center = self.scroll_into_view().await?.clickable_point().await?;
284+
self.tab.click_with(center, options).await?;
285+
Ok(self)
286+
}
287+
274288
/// Type the input
275289
///
276290
/// # Example type text into an input element

src/handler/page.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,21 @@ impl PageInner {
191191

192192
/// Performs a mouse click event at the point's location
193193
pub async fn click(&self, point: Point) -> Result<&Self> {
194+
let default_opts = chromiumoxide_types::ClickOptions::default();
195+
self.click_with(point, default_opts).await
196+
}
197+
198+
/// Performs a mouse click event at the point's location with custom options
199+
pub async fn click_with(
200+
&self,
201+
point: Point,
202+
options: chromiumoxide_types::ClickOptions,
203+
) -> Result<&Self> {
194204
let cmd = DispatchMouseEventParams::builder()
195205
.x(point.x)
196206
.y(point.y)
197207
.button(MouseButton::Left)
198-
.click_count(1);
208+
.click_count(options.click_count);
199209

200210
self.move_mouse(point)
201211
.await?

src/page.rs

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ impl Page {
507507
/// immediately loaded when `click()` resolves. To wait until navigation is
508508
/// finished an additional `wait_for_navigation()` is required:
509509
///
510-
/// # Example
510+
/// # Examples
511511
///
512512
/// Trigger a navigation and wait until the triggered navigation is finished
513513
///
@@ -521,9 +521,28 @@ impl Page {
521521
/// # }
522522
/// ```
523523
///
524-
/// # Example
525524
///
526-
/// Perform custom click
525+
/// Use [`click_with()`] to perform a custom click:
526+
///
527+
/// ```no_run
528+
/// # use chromiumoxide::page::Page;
529+
/// # use chromiumoxide::error::Result;
530+
/// # use chromiumoxide::layout::Point;
531+
/// # use chromiumoxide::types::ClickOptions;
532+
/// # async fn demo(page: Page, point: Point) -> Result<()> {
533+
/// let options = ClickOptions::builder()
534+
/// .click_count(2)
535+
/// .build();
536+
///
537+
/// page.click_with(point, options).await?;
538+
/// # Ok(())
539+
/// # }
540+
/// ```
541+
///
542+
/// ## Advanced
543+
///
544+
/// For advanced use cases, the same behavior can be achieved manually by
545+
/// issuing `DispatchMouseEventParams` commands directly via the CDP API.
527546
///
528547
/// ```no_run
529548
/// # use chromiumoxide::page::Page;
@@ -561,6 +580,46 @@ impl Page {
561580
Ok(self)
562581
}
563582

583+
/// Performs a mouse click event at the point's location using the provided
584+
/// [`ClickOptions`].
585+
///
586+
/// This behaves the same as [`click()`], but allows customizing click behavior
587+
/// such as click count or other click-related options.
588+
///
589+
/// The point is scrolled into view first, then the corresponding
590+
/// `DispatchMouseEventParams` commands are issued according to the supplied
591+
/// options.
592+
///
593+
/// Bear in mind that if `click_with()` triggers a navigation, the new page is
594+
/// not immediately loaded when this function resolves. To wait until navigation
595+
/// is finished, an additional [`wait_for_navigation()`] is required.
596+
///
597+
/// # Example
598+
///
599+
/// Perform a double click using [`ClickOptions`]
600+
///
601+
/// ```no_run
602+
/// # use chromiumoxide::page::Page;
603+
/// # use chromiumoxide::error::Result;
604+
/// # use chromiumoxide::layout::Point;
605+
/// # use chromiumoxide::types::ClickOptions;
606+
/// # async fn demo(page: Page, point: Point) -> Result<()> {
607+
/// let options = ClickOptions::builder()
608+
/// .click_count(2)
609+
/// .build();
610+
///
611+
/// page.click_with(point, options)
612+
/// .await?
613+
/// .wait_for_navigation()
614+
/// .await?;
615+
/// # Ok(())
616+
/// # }
617+
/// ```
618+
pub async fn click_with(&self, point: Point, options: ClickOptions) -> Result<&Self> {
619+
self.inner.click_with(point, options).await?;
620+
Ok(self)
621+
}
622+
564623
/// Dispatches a `mousemove` event and moves the mouse to the position of
565624
/// the `point` where `Point.x` is the horizontal position of the mouse and
566625
/// `Point.y` the vertical position of the mouse.

0 commit comments

Comments
 (0)