Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

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

## [0.8.0] 2025-11-28

Expand Down
47 changes: 47 additions & 0 deletions chromiumoxide_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,50 @@ impl From<String> for Binary {
Self(expr)
}
}

#[derive(Clone, Debug)]
pub struct ClickOptions {
pub click_count: i64,
}

impl Default for ClickOptions {
fn default() -> Self {
Self { click_count: 1 }
}
}

impl ClickOptions {
pub fn new() -> Self {
Self::default()
}
pub fn builder() -> ClickOptionsBuilder {
ClickOptionsBuilder::default()
}
}

#[derive(Clone, Debug)]
pub struct ClickOptionsBuilder {
click_count: i64,
}

impl Default for ClickOptionsBuilder {
fn default() -> Self {
Self { click_count: 1 }
}
}

impl ClickOptionsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn click_count(mut self, count: impl Into<i64>) -> Self {
self.click_count = count.into();
self
}

pub fn build(self) -> ClickOptions {
ClickOptions {
click_count: self.click_count,
}
}
}
16 changes: 15 additions & 1 deletion src/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};

use chromiumoxide_types::ClickOptions;
use futures::{future, Future, FutureExt, Stream};

use chromiumoxide_cdp::cdp::browser_protocol::dom::{
Expand Down Expand Up @@ -267,7 +268,20 @@ impl Element {
/// not exist anymore.
pub async fn click(&self) -> Result<&Self> {
let center = self.scroll_into_view().await?.clickable_point().await?;
self.tab.click(center).await?;
self.tab.click(center, ClickOptions::default()).await?;
Ok(self)
}

/// Clicks the element using the provided [`ClickOptions`].
///
/// This behaves the same as [`click()`], but allows customizing
/// click behavior such as click count.
///
/// Note that if the click triggers a navigation, this element
/// may no longer exist afterwards.
pub async fn click_with(&self,options: ClickOptions) -> Result<&Self> {
let center = self.scroll_into_view().await?.clickable_point().await?;
self.tab.click(center, options).await?;
Ok(self)
}

Expand Down
4 changes: 2 additions & 2 deletions src/handler/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,12 @@ impl PageInner {
}

/// Performs a mouse click event at the point's location
pub async fn click(&self, point: Point) -> Result<&Self> {
pub async fn click(&self, point: Point, options: chromiumoxide_types::ClickOptions) -> Result<&Self> {
let cmd = DispatchMouseEventParams::builder()
.x(point.x)
.y(point.y)
.button(MouseButton::Left)
.click_count(1);
.click_count(options.click_count);

self.move_mouse(point)
.await?
Expand Down
72 changes: 68 additions & 4 deletions src/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ impl Page {
/// immediately loaded when `click()` resolves. To wait until navigation is
/// finished an additional `wait_for_navigation()` is required:
///
/// # Example
/// # Examples
///
/// Trigger a navigation and wait until the triggered navigation is finished
///
Expand All @@ -521,10 +521,29 @@ impl Page {
/// # }
/// ```
///
/// # Example
///
/// Perform custom click
/// Use [`click_with()`] to perform a custom click:
///
/// ```no_run
/// # use chromiumoxide::page::Page;
/// # use chromiumoxide::error::Result;
/// # use chromiumoxide::layout::Point;
/// # use chromiumoxide::types::ClickOptions;
/// # async fn demo(page: Page, point: Point) -> Result<()> {
/// let options = ClickOptions::builder()
/// .click_count(2)
/// .build();
///
/// page.click_with(point, options).await?;
/// # Ok(())
/// # }
/// ```
///
/// ## Advanced
///
/// For advanced use cases, the same behavior can be achieved manually by
/// issuing `DispatchMouseEventParams` commands directly via the CDP API.
///
/// ```no_run
/// # use chromiumoxide::page::Page;
/// # use chromiumoxide::error::Result;
Expand Down Expand Up @@ -557,7 +576,51 @@ impl Page {
/// # }
/// ```
pub async fn click(&self, point: Point) -> Result<&Self> {
self.inner.click(point).await?;
self.inner.click(point, ClickOptions::default()).await?;
Ok(self)
}

/// Performs a mouse click event at the point's location using the provided
/// [`ClickOptions`].
///
/// This behaves the same as [`click()`], but allows customizing click behavior
/// such as click count or other click-related options.
///
/// The point is scrolled into view first, then the corresponding
/// `DispatchMouseEventParams` commands are issued according to the supplied
/// options.
///
/// Bear in mind that if `click_with()` triggers a navigation, the new page is
/// not immediately loaded when this function resolves. To wait until navigation
/// is finished, an additional [`wait_for_navigation()`] is required.
///
/// # Example
///
/// Perform a double click using [`ClickOptions`]
///
/// ```no_run
/// # use chromiumoxide::page::Page;
/// # use chromiumoxide::error::Result;
/// # use chromiumoxide::layout::Point;
/// # use chromiumoxide::types::ClickOptions;
/// # async fn demo(page: Page, point: Point) -> Result<()> {
/// let options = ClickOptions::builder()
/// .click_count(2)
/// .build();
///
/// page.click_with(point, options)
/// .await?
/// .wait_for_navigation()
/// .await?;
/// # Ok(())
/// # }
/// ```
pub async fn click_with(
&self,
point: Point,
options: ClickOptions,
) -> Result<&Self> {
self.inner.click(point, options).await?;
Ok(self)
}

Expand Down Expand Up @@ -1411,3 +1474,4 @@ impl From<MediaTypeParams> for String {
}
}
}