Type parameter `Message` must be used as the type parameter for some local type

Hello,

I'm trying to implement my own ui component of Coffee::ui. I am a beginner, so I copied existing radio component and modified. My goal is to write a text input.

There is the code:

//! Create text input.
use coffee::graphics::{
    Color, HorizontalAlignment, Point, Rectangle, VerticalAlignment,
};
use coffee::input::{mouse, keyboard, ButtonState};
use coffee::ui::core::{
    Align, Element, Event, Hasher, Layout, MouseCursor, Node, Widget,
};
use coffee::ui::widget::{text, Column, Row, Text};

use std::hash::Hash;

pub struct TextInput<Message> {
    is_selected: bool,
    on_change: Message,
    label: String,
    value: String,
    label_color: Color,
}

impl<Message> std::fmt::Debug for TextInput<Message>
where
    Message: std::fmt::Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TextInput")
            .field("is_selected", &self.is_selected)
            .field("on_change", &self.on_change)
            .field("label", &self.label)
            .field("value", &self.value)
            .field("label_color", &self.label_color)
            .finish()
    }
}

impl<Message> TextInput<Message> {
    pub fn new<F>(label: &str, value: &str, is_selected: bool, f: F) -> Self
    where
        F: 'static + Fn(&str) -> Message,
    {
        TextInput {
            is_selected,
            on_change: f,
            label: String::from(label),
            value: String::from(value),
            label_color: Color::WHITE,
        }
    }

    pub fn label_color(mut self, color: Color) -> Self {
        self.label_color = color;
        self
    }
}

impl<Message, Renderer> Widget<Message, Renderer> for TextInput<Message>
where
    Renderer: self::Renderer + text::Renderer,
    Message: Copy + std::fmt::Debug,
{
    fn node(&self, renderer: &Renderer) -> Node {
        Row::<(), Renderer>::new()
            .spacing(15)
            .align_items(Align::Center)
            .push(Column::new().width(28).height(28))
            .push(Text::new(format!("{}: {}", self.label, self.value).as_str()))
            .node(renderer)
    }

    fn on_event(
        &mut self,
        event: Event,
        layout: Layout<'_>,
        cursor_position: Point,
        messages: &mut Vec<Message>,
    ) {
        match event {
            Event::Mouse(mouse::Event::Input {
                button: mouse::Button::Left,
                state: ButtonState::Pressed,
            }) => {
                if layout.bounds().contains(cursor_position) {
                    self.is_selected = true;
                } else {
                    self.is_selected = false;
                }
            }
            Event::Keyboard(keyboard::Event::Input { state, key_code }) => {
                // FIXME BS NOW: real input key, see example/input.rs ?
                self.value = format!("{}{}", self.value, "X");
            }
            _ => {}
        }
    }

    fn draw(
        &self,
        renderer: &mut Renderer,
        layout: Layout<'_>,
        cursor_position: Point,
    ) -> MouseCursor {
        let children: Vec<_> = layout.children().collect();

        let mut text_bounds = children[1].bounds();
        text_bounds.y -= 2.0;

        text::Renderer::draw(
            renderer,
            text_bounds,
            format!("{}: {}", self.label, self.value).as_str(),
            20.0,
            self.label_color,
            HorizontalAlignment::Left,
            VerticalAlignment::Top,
        );

        self::Renderer::draw(
            renderer,
            cursor_position,
            children[0].bounds(),
            layout.bounds(),
            self.is_selected,
        )
    }

    fn hash(&self, state: &mut Hasher) {
        self.label.hash(state);
    }
}


pub trait Renderer {
    fn draw(
        &mut self,
        cursor_position: Point,
        bounds: Rectangle<f32>,
        label_bounds: Rectangle<f32>,
        is_selected: bool,
    ) -> MouseCursor;
}

impl<'a, Message, Renderer> From<TextInput<Message>>
    for Element<'a, Message, Renderer>
where
    Renderer: self::Renderer + text::Renderer,
    Message: 'static + Copy + std::fmt::Debug,
{
    fn from(text_input: TextInput<Message>) -> Element<'a, Message, Renderer> {
        Element::new(text_input)
    }
}

Error happen at compile time:

error[E0210]: type parameter `Message` must be used as the type parameter for some local type (e.g., `MyStruct<Message>`)
   --> src/ui/text_input.rs:143:10
    |
143 | impl<'a, Message, Renderer> From<TextInput<Message>>
    |          ^^^^^^^ type parameter `Message` must be used as the type parameter for some local type
    |
    = note: only traits defined in the current crate can be implemented for a type parameter

error: aborting due to previous error

I don't understand what i have to do. Any help ? thank you so much by advance !

The error message tells you:

note: only traits defined in the current crate can be implemented for a type parameter

You are trying to implement From for Element, both of which are defined in external crates. This looks like an instance of the orphan rule, which basically says external traits cannot be implemented on external types. I know that the rules was relaxed a bit in a recent compiler release. But there is still ongoing work to relax the rules further: https://github.com/rust-lang/rfcs/issues/1856

There is apparently a lot to research on this topic (I have little experience here) for example https://github.com/Ixrec/rust-orphan-rules has a description of the problem and contains some reference links with additional info.

Maybe the easiest thing to do in this case is adding your text input directly to coffee so your code has type information for Element?

Hello parasyte,

I see. I trying it as you recommand: https://github.com/buxx/coffee/blob/dev/text_input/src/ui/widget/text_input.rs

And it is working. Is it a design failure of coffee to not permit extending ?

I think it is just a limitation in the Rust type system at the moment. The intention for coffee (and iced which is derived from coffee's UI implementation) is to allow extending the UI. It might be worth creating a tracking ticket in coffee (or probably iced instead?) just to put visibility on this issue.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.