mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 14:21:16 +00:00
Use ComponentRc during item tree traversal
This is in preparation for allowing the run-time / items to clone VRc's of the component. ComponentVTable functions like visit_children_item contine to take a ComponentRefPin as "self" parameter type, as a VRc would not be supported by rust right now. That means the implementation then uses self_weak to obtain a strong self-reference.
This commit is contained in:
parent
54cc66c400
commit
78fae068dd
9 changed files with 72 additions and 49 deletions
|
@ -62,6 +62,7 @@ using ComponentRef = vtable::VRef<private_api::ComponentVTable>;
|
|||
using ItemRef = vtable::VRef<private_api::ItemVTable>;
|
||||
using ItemVisitorRefMut = vtable::VRefMut<cbindgen_private::ItemVisitorVTable>;
|
||||
}
|
||||
using cbindgen_private::ComponentRc;
|
||||
using cbindgen_private::EasingCurve;
|
||||
using cbindgen_private::PropertyAnimation;
|
||||
using cbindgen_private::Slice;
|
||||
|
@ -174,7 +175,7 @@ using cbindgen_private::MouseEvent;
|
|||
using cbindgen_private::sixtyfps_visit_item_tree;
|
||||
namespace private_api {
|
||||
template<typename GetDynamic>
|
||||
inline InputEventResult process_input_event(ComponentRef component, int64_t &mouse_grabber,
|
||||
inline InputEventResult process_input_event(const ComponentRc &component_rc, int64_t &mouse_grabber,
|
||||
MouseEvent mouse_event, Slice<ItemTreeNode> tree,
|
||||
GetDynamic get_dynamic, const ComponentWindow *window,
|
||||
const ComponentRef *app_component)
|
||||
|
@ -182,7 +183,8 @@ inline InputEventResult process_input_event(ComponentRef component, int64_t &mou
|
|||
if (mouse_grabber != -1) {
|
||||
auto item_index = mouse_grabber & 0xffffffff;
|
||||
auto rep_index = mouse_grabber >> 32;
|
||||
auto offset = cbindgen_private::sixtyfps_item_offset(component, tree, item_index);
|
||||
auto offset =
|
||||
cbindgen_private::sixtyfps_item_offset(component_rc.borrow(), tree, item_index);
|
||||
mouse_event.pos = { mouse_event.pos.x - offset.x, mouse_event.pos.y - offset.y };
|
||||
const auto &item_node = tree.ptr[item_index];
|
||||
InputEventResult result = InputEventResult::EventIgnored;
|
||||
|
@ -191,7 +193,7 @@ inline InputEventResult process_input_event(ComponentRef component, int64_t &mou
|
|||
result = item_node.item.item.vtable->input_event(
|
||||
{
|
||||
item_node.item.item.vtable,
|
||||
reinterpret_cast<char *>(component.instance)
|
||||
reinterpret_cast<char *>(component_rc.borrow().instance)
|
||||
+ item_node.item.item.offset,
|
||||
},
|
||||
mouse_event, window, *app_component);
|
||||
|
@ -207,7 +209,7 @@ inline InputEventResult process_input_event(ComponentRef component, int64_t &mou
|
|||
return result;
|
||||
} else {
|
||||
return cbindgen_private::sixtyfps_process_ungrabbed_mouse_event(
|
||||
component, mouse_event, window, *app_component, &mouse_grabber);
|
||||
&component_rc, mouse_event, window, *app_component, &mouse_grabber);
|
||||
}
|
||||
}
|
||||
template<typename GetDynamic>
|
||||
|
@ -238,14 +240,14 @@ inline KeyEventResult process_key_event(ComponentRef component, int64_t focus_it
|
|||
}
|
||||
|
||||
template<typename GetDynamic>
|
||||
inline FocusEventResult process_focus_event(ComponentRef component, int64_t &focus_item,
|
||||
inline FocusEventResult process_focus_event(const ComponentRc &component_rc, int64_t &focus_item,
|
||||
const FocusEvent *event, Slice<ItemTreeNode> tree,
|
||||
GetDynamic get_dynamic, const ComponentWindow *window)
|
||||
{
|
||||
switch (event->tag) {
|
||||
case FocusEvent::Tag::FocusIn:
|
||||
return cbindgen_private::sixtyfps_locate_and_activate_focus_item(component, event, window,
|
||||
&focus_item);
|
||||
return cbindgen_private::sixtyfps_locate_and_activate_focus_item(&component_rc, event,
|
||||
window, &focus_item);
|
||||
case FocusEvent::Tag::FocusOut:
|
||||
[[fallthrough]];
|
||||
case FocusEvent::Tag::WindowReceivedFocus:
|
||||
|
@ -260,7 +262,7 @@ inline FocusEventResult process_focus_event(ComponentRef component, int64_t &foc
|
|||
item_node.item.item.vtable->focus_event(
|
||||
{
|
||||
item_node.item.item.vtable,
|
||||
reinterpret_cast<char *>(component.instance)
|
||||
reinterpret_cast<char *>(component_rc.borrow().instance)
|
||||
+ item_node.item.item.offset,
|
||||
},
|
||||
event, window);
|
||||
|
|
|
@ -74,6 +74,8 @@ private:
|
|||
X data;
|
||||
Layout layout;
|
||||
};
|
||||
|
||||
void *data_ptr() { return reinterpret_cast<char *>(this) + data_offset; }
|
||||
};
|
||||
|
||||
struct Dyn {};
|
||||
|
@ -120,6 +122,8 @@ public:
|
|||
}
|
||||
|
||||
VRc<VTable, Dyn> into_dyn() const { return *reinterpret_cast<const VRc<VTable, Dyn> *>(this); }
|
||||
|
||||
VRef<VTable> borrow() const { return { inner->vtable, inner->data_ptr() }; }
|
||||
};
|
||||
|
||||
template<typename VTable, typename X = Dyn>
|
||||
|
|
|
@ -1020,7 +1020,8 @@ fn generate_component(
|
|||
format!(" [[maybe_unused]] auto self = reinterpret_cast<const {}*>(base);", component_id),
|
||||
format!(" switch(dyn_index) {{ {} }};", children_visitor_cases.join("")),
|
||||
" std::abort();\n};".to_owned(),
|
||||
"return sixtyfps::sixtyfps_visit_item_tree(component, item_tree() , index, order, visitor, dyn_visit);".to_owned(),
|
||||
format!("auto self_rc = reinterpret_cast<const {}*>(component.instance)->self_weak.lock()->into_dyn();", component_id),
|
||||
"return sixtyfps::sixtyfps_visit_item_tree(&self_rc, item_tree() , index, order, visitor, dyn_visit);".to_owned(),
|
||||
]),
|
||||
..Default::default()
|
||||
}),
|
||||
|
@ -1073,7 +1074,8 @@ fn generate_component(
|
|||
is_static: true,
|
||||
statements: Some(vec {".into(),
|
||||
"auto self_rc = self->self_weak.lock()->into_dyn();".into(),
|
||||
"return sixtyfps::private_api::process_input_event(self_rc, self->mouse_grabber, mouse_event, item_tree(), [self](int dyn_index, [[maybe_unused]] int rep_index) {".into(),
|
||||
" (void)self;".into(),
|
||||
format!(" switch(dyn_index) {{ {} }};", repeated_input_branch.join("")),
|
||||
" return sixtyfps::private_api::ComponentRef{nullptr, nullptr};\n}, window, app_component);".into(),
|
||||
|
@ -1119,7 +1121,8 @@ fn generate_component(
|
|||
is_static: true,
|
||||
statements: Some(vec {".into(),
|
||||
"auto self_rc = self->self_weak.lock()->into_dyn();".into(),
|
||||
"return sixtyfps::private_api::process_focus_event(self_rc, self->focus_item, focus_event, item_tree(), [self](int dyn_index, [[maybe_unused]] int rep_index) {".into(),
|
||||
" (void)self;".into(),
|
||||
format!(" switch(dyn_index) {{ {} }};", repeated_input_branch.join("")),
|
||||
" return sixtyfps::private_api::ComponentRef{nullptr, nullptr};\n}, window);".into(),
|
||||
|
|
|
@ -651,7 +651,7 @@ fn generate_component(
|
|||
-> sixtyfps::re_exports::VisitChildrenResult
|
||||
{
|
||||
use sixtyfps::re_exports::*;
|
||||
return sixtyfps::re_exports::visit_item_tree(self, VRef::new_pin(self), Self::item_tree(), index, order, visitor, visit_dynamic);
|
||||
return sixtyfps::re_exports::visit_item_tree(self, &VRc::into_dyn(self.as_ref().self_weak.get().unwrap().upgrade().unwrap()), Self::item_tree(), index, order, visitor, visit_dynamic);
|
||||
#[allow(unused)]
|
||||
fn visit_dynamic(self_pinned: ::core::pin::Pin<&#component_id>, order: sixtyfps::re_exports::TraversalOrder, visitor: ItemVisitorRefMut, dyn_index: usize) -> VisitChildrenResult {
|
||||
let _self = self_pinned;
|
||||
|
@ -688,7 +688,7 @@ fn generate_component(
|
|||
_ => (res, VisitChildrenResult::CONTINUE),
|
||||
}
|
||||
} else {
|
||||
process_ungrabbed_mouse_event(VRef::new_pin(self), mouse_event, window, app_component.clone())
|
||||
process_ungrabbed_mouse_event(&VRc::into_dyn(self.as_ref().self_weak.get().unwrap().upgrade().unwrap()), mouse_event, window, app_component.clone())
|
||||
};
|
||||
self.mouse_grabber.set(new_grab);
|
||||
status
|
||||
|
@ -722,7 +722,7 @@ fn generate_component(
|
|||
#[allow(unused)]
|
||||
match event {
|
||||
FocusEvent::FocusIn(_) => {
|
||||
let (event_result, visit_result) = locate_and_activate_focus_item(VRef::new_pin(self), event, window);
|
||||
let (event_result, visit_result) = locate_and_activate_focus_item(&VRc::into_dyn(self.as_ref().self_weak.get().unwrap().upgrade().unwrap()), event, window);
|
||||
if event_result == FocusEventResult::FocusItemFound {
|
||||
self.focus_item.set(visit_result)
|
||||
}
|
||||
|
|
|
@ -609,8 +609,8 @@ impl<Backend: GraphicsBackend> crate::eventloop::GenericWindow for GraphicsWindo
|
|||
}
|
||||
|
||||
fn draw(self: Rc<Self>) {
|
||||
let component = self.component.borrow().upgrade().unwrap();
|
||||
let component = ComponentRc::borrow_pin(&component);
|
||||
let component_rc = self.component.borrow().upgrade().unwrap();
|
||||
let component = ComponentRc::borrow_pin(&component_rc);
|
||||
|
||||
{
|
||||
if self.layout_listener.as_ref().is_dirty() {
|
||||
|
@ -629,7 +629,7 @@ impl<Backend: GraphicsBackend> crate::eventloop::GenericWindow for GraphicsWindo
|
|||
|
||||
// Generate cached rendering data once
|
||||
crate::item_tree::visit_items(
|
||||
component,
|
||||
&component_rc,
|
||||
crate::item_tree::TraversalOrder::BackToFront,
|
||||
|_, item, _| {
|
||||
crate::item_rendering::update_item_rendering_data(
|
||||
|
@ -656,7 +656,7 @@ impl<Backend: GraphicsBackend> crate::eventloop::GenericWindow for GraphicsWindo
|
|||
&ARGBColor { red: 255 as u8, green: 255, blue: 255, alpha: 255 }.into(),
|
||||
);
|
||||
crate::item_rendering::render_component_items(
|
||||
component,
|
||||
&component_rc,
|
||||
&mut frame,
|
||||
&window.rendering_cache,
|
||||
&self,
|
||||
|
|
|
@ -11,7 +11,7 @@ LICENSE END */
|
|||
*/
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use crate::component::ComponentRefPin;
|
||||
use crate::component::{ComponentRc, ComponentRefPin};
|
||||
use crate::graphics::Point;
|
||||
use crate::item_tree::{ItemVisitorResult, VisitChildrenResult};
|
||||
use euclid::default::Vector2D;
|
||||
|
@ -480,7 +480,7 @@ pub enum FocusEventResult {
|
|||
/// the focus_event (assuming it is of type FocusIn). Once located, the focus in
|
||||
/// even will also be dispatched to the item itself.
|
||||
pub fn locate_and_activate_focus_item(
|
||||
component: ComponentRefPin,
|
||||
component: &ComponentRc,
|
||||
focus_event: &FocusEvent,
|
||||
window: &crate::eventloop::ComponentWindow,
|
||||
) -> (FocusEventResult, VisitChildrenResult) {
|
||||
|
@ -522,7 +522,7 @@ pub fn locate_and_activate_focus_item(
|
|||
/// * `component`: The component to deliver the event to.
|
||||
/// * `event`: The mouse event to deliver.
|
||||
pub fn process_ungrabbed_mouse_event(
|
||||
component: ComponentRefPin,
|
||||
component: &ComponentRc,
|
||||
event: MouseEvent,
|
||||
window: &crate::eventloop::ComponentWindow,
|
||||
app_component: ComponentRefPin,
|
||||
|
@ -596,7 +596,7 @@ pub(crate) mod ffi {
|
|||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn sixtyfps_process_ungrabbed_mouse_event(
|
||||
component: core::pin::Pin<crate::component::ComponentRef>,
|
||||
component: &ComponentRc,
|
||||
event: MouseEvent,
|
||||
window: &crate::eventloop::ComponentWindow,
|
||||
app_component: core::pin::Pin<crate::component::ComponentRef>,
|
||||
|
@ -620,7 +620,7 @@ pub(crate) mod ffi {
|
|||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn sixtyfps_locate_and_activate_focus_item(
|
||||
component: core::pin::Pin<crate::component::ComponentRef>,
|
||||
component: &ComponentRc,
|
||||
event: &FocusEvent,
|
||||
window: &crate::eventloop::ComponentWindow,
|
||||
new_focus_item: &mut crate::item_tree::VisitChildrenResult,
|
||||
|
|
|
@ -14,8 +14,10 @@ use super::graphics::{
|
|||
Frame, GraphicsBackend, GraphicsWindow, RenderingCache, RenderingPrimitivesBuilder,
|
||||
};
|
||||
use super::items::ItemRef;
|
||||
use crate::component::ComponentRc;
|
||||
use crate::eventloop::ComponentWindow;
|
||||
use crate::item_tree::ItemVisitorResult;
|
||||
use crate::{eventloop::ComponentWindow, slice::Slice};
|
||||
use crate::slice::Slice;
|
||||
use cgmath::{Matrix4, SquareMatrix, Vector3};
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
|
@ -80,7 +82,7 @@ pub(crate) fn update_item_rendering_data<Backend: GraphicsBackend>(
|
|||
}
|
||||
|
||||
pub(crate) fn render_component_items<Backend: GraphicsBackend>(
|
||||
component: crate::component::ComponentRefPin,
|
||||
component: &ComponentRc,
|
||||
frame: &mut Backend::Frame,
|
||||
rendering_cache: &RefCell<RenderingCache<Backend>>,
|
||||
window: &std::rc::Rc<GraphicsWindow<Backend>>,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
This file is also available under commercial licensing terms.
|
||||
Please contact info@sixtyfps.io for more information.
|
||||
LICENSE END */
|
||||
use crate::component::{ComponentRefPin, ComponentVTable};
|
||||
use crate::component::{ComponentRc, ComponentVTable};
|
||||
use crate::graphics::Point;
|
||||
use crate::items::{ItemRef, ItemVTable};
|
||||
use core::pin::Pin;
|
||||
|
@ -106,7 +106,7 @@ pub struct ItemVisitorVTable {
|
|||
/// and `item` is a reference to the item itself
|
||||
visit_item: fn(
|
||||
VRefMut<ItemVisitorVTable>,
|
||||
component: Pin<VRef<ComponentVTable>>,
|
||||
component: &VRc<ComponentVTable, vtable::Dyn>,
|
||||
index: isize,
|
||||
item: Pin<VRef<ItemVTable>>,
|
||||
) -> VisitChildrenResult,
|
||||
|
@ -117,12 +117,10 @@ pub struct ItemVisitorVTable {
|
|||
/// Type alias to `vtable::VRefMut<ItemVisitorVTable>`
|
||||
pub type ItemVisitorRefMut<'a> = vtable::VRefMut<'a, ItemVisitorVTable>;
|
||||
|
||||
impl<T: FnMut(crate::component::ComponentRefPin, isize, Pin<ItemRef>) -> VisitChildrenResult>
|
||||
ItemVisitor for T
|
||||
{
|
||||
impl<T: FnMut(&ComponentRc, isize, Pin<ItemRef>) -> VisitChildrenResult> ItemVisitor for T {
|
||||
fn visit_item(
|
||||
&mut self,
|
||||
component: crate::component::ComponentRefPin,
|
||||
component: &ComponentRc,
|
||||
index: isize,
|
||||
item: Pin<ItemRef>,
|
||||
) -> VisitChildrenResult {
|
||||
|
@ -140,9 +138,9 @@ pub enum ItemVisitorResult<State> {
|
|||
///
|
||||
/// Returns the index of the item that cancelled, or -1 if nobody cancelled
|
||||
pub fn visit_items<State>(
|
||||
component: ComponentRefPin,
|
||||
component: &ComponentRc,
|
||||
order: TraversalOrder,
|
||||
mut visitor: impl FnMut(ComponentRefPin, Pin<ItemRef>, &State) -> ItemVisitorResult<State>,
|
||||
mut visitor: impl FnMut(&ComponentRc, Pin<ItemRef>, &State) -> ItemVisitorResult<State>,
|
||||
state: State,
|
||||
) -> VisitChildrenResult {
|
||||
visit_internal(
|
||||
|
@ -161,33 +159,33 @@ pub fn visit_items<State>(
|
|||
///
|
||||
/// Returns the index of the item that cancelled, or -1 if nobody cancelled
|
||||
pub fn visit_items_with_post_visit<State, PostVisitState>(
|
||||
component: ComponentRefPin,
|
||||
component: &ComponentRc,
|
||||
order: TraversalOrder,
|
||||
mut visitor: impl FnMut(
|
||||
ComponentRefPin,
|
||||
&ComponentRc,
|
||||
Pin<ItemRef>,
|
||||
&State,
|
||||
) -> (ItemVisitorResult<State>, PostVisitState),
|
||||
mut post_visitor: impl FnMut(ComponentRefPin, Pin<ItemRef>, PostVisitState),
|
||||
mut post_visitor: impl FnMut(&ComponentRc, Pin<ItemRef>, PostVisitState),
|
||||
state: State,
|
||||
) -> VisitChildrenResult {
|
||||
visit_internal(component, order, &mut visitor, &mut post_visitor, -1, &state)
|
||||
}
|
||||
|
||||
fn visit_internal<State, PostVisitState>(
|
||||
component: ComponentRefPin,
|
||||
component: &ComponentRc,
|
||||
order: TraversalOrder,
|
||||
visitor: &mut impl FnMut(
|
||||
ComponentRefPin,
|
||||
&ComponentRc,
|
||||
Pin<ItemRef>,
|
||||
&State,
|
||||
) -> (ItemVisitorResult<State>, PostVisitState),
|
||||
post_visitor: &mut impl FnMut(ComponentRefPin, Pin<ItemRef>, PostVisitState),
|
||||
post_visitor: &mut impl FnMut(&ComponentRc, Pin<ItemRef>, PostVisitState),
|
||||
index: isize,
|
||||
state: &State,
|
||||
) -> VisitChildrenResult {
|
||||
let mut actual_visitor =
|
||||
|component: ComponentRefPin, index: isize, item: Pin<ItemRef>| -> VisitChildrenResult {
|
||||
|component: &ComponentRc, index: isize, item: Pin<ItemRef>| -> VisitChildrenResult {
|
||||
match visitor(component, item, state) {
|
||||
(ItemVisitorResult::Continue(state), post_visit_state) => {
|
||||
let result =
|
||||
|
@ -199,7 +197,7 @@ fn visit_internal<State, PostVisitState>(
|
|||
}
|
||||
};
|
||||
vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
|
||||
component.as_ref().visit_children_item(index, order, actual_visitor)
|
||||
VRc::borrow_pin(component).as_ref().visit_children_item(index, order, actual_visitor)
|
||||
}
|
||||
|
||||
/// Visit the children within an array of ItemTreeNode
|
||||
|
@ -212,7 +210,7 @@ fn visit_internal<State, PostVisitState>(
|
|||
/// Possibly we should generate code that directly call the visitor instead
|
||||
pub fn visit_item_tree<Base>(
|
||||
base: Pin<&Base>,
|
||||
component: ComponentRefPin,
|
||||
component: &ComponentRc,
|
||||
item_tree: &[ItemTreeNode<Base>],
|
||||
index: isize,
|
||||
order: TraversalOrder,
|
||||
|
@ -307,7 +305,7 @@ pub(crate) mod ffi {
|
|||
/// Safety: Assume a correct implementation of the item_tree array
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_visit_item_tree(
|
||||
component: Pin<VRef<ComponentVTable>>,
|
||||
component: &ComponentRc,
|
||||
item_tree: Slice<ItemTreeNode<u8>>,
|
||||
index: isize,
|
||||
order: TraversalOrder,
|
||||
|
@ -320,7 +318,7 @@ pub(crate) mod ffi {
|
|||
) -> VisitChildrenResult,
|
||||
) -> VisitChildrenResult {
|
||||
crate::item_tree::visit_item_tree(
|
||||
Pin::new_unchecked(&*(component.as_ptr() as *const u8)),
|
||||
Pin::new_unchecked(&*(&**component as *const Dyn as *const u8)),
|
||||
component,
|
||||
item_tree.as_slice(),
|
||||
index,
|
||||
|
|
|
@ -336,9 +336,10 @@ extern "C" fn visit_children_item(
|
|||
) -> VisitChildrenResult {
|
||||
generativity::make_guard!(guard);
|
||||
let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
|
||||
let comp_rc = instance_ref.self_weak().get().unwrap().upgrade().unwrap();
|
||||
sixtyfps_corelib::item_tree::visit_item_tree(
|
||||
instance_ref.instance,
|
||||
component,
|
||||
&vtable::VRc::into_dyn(comp_rc),
|
||||
instance_ref.component_type.item_tree.as_slice().into(),
|
||||
index,
|
||||
order,
|
||||
|
@ -1014,6 +1015,7 @@ pub fn instantiate<'id>(
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
comp_rc
|
||||
}
|
||||
|
||||
|
@ -1459,8 +1461,12 @@ extern "C" fn input_event(
|
|||
_ => (res, VisitChildrenResult::CONTINUE),
|
||||
}
|
||||
} else {
|
||||
generativity::make_guard!(guard);
|
||||
let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
|
||||
let comp_rc = instance_ref.self_weak().get().unwrap().upgrade().unwrap();
|
||||
|
||||
sixtyfps_corelib::input::process_ungrabbed_mouse_event(
|
||||
component,
|
||||
&vtable::VRc::into_dyn(comp_rc),
|
||||
mouse_event,
|
||||
window,
|
||||
app_component.clone(),
|
||||
|
@ -1508,8 +1514,16 @@ extern "C" fn focus_event(
|
|||
|
||||
match event {
|
||||
FocusEvent::FocusIn(_) => {
|
||||
generativity::make_guard!(guard);
|
||||
let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
|
||||
let comp_rc = instance_ref.self_weak().get().unwrap().upgrade().unwrap();
|
||||
|
||||
let (event_result, visit_result) =
|
||||
sixtyfps_corelib::input::locate_and_activate_focus_item(component, event, window);
|
||||
sixtyfps_corelib::input::locate_and_activate_focus_item(
|
||||
&vtable::VRc::into_dyn(comp_rc),
|
||||
event,
|
||||
window,
|
||||
);
|
||||
if event_result == FocusEventResult::FocusItemFound {
|
||||
extra_data.focus_item.set(visit_result)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue