mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-06 03:29:44 -06:00
Merge pull request #2094 from OpenSpace/feature/properties-visibility
Add settings in OpenSpaceEngine that sets visibility in gui
This commit is contained in:
@@ -25,6 +25,9 @@
|
||||
#ifndef __OPENSPACE_CORE___OPENSPACEENGINE___H__
|
||||
#define __OPENSPACE_CORE___OPENSPACEENGINE___H__
|
||||
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
#include <openspace/properties/property.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/scene/profile.h>
|
||||
@@ -63,7 +66,7 @@ struct CommandlineArguments {
|
||||
std::string configurationOverride;
|
||||
};
|
||||
|
||||
class OpenSpaceEngine {
|
||||
class OpenSpaceEngine : public properties::PropertyOwner {
|
||||
public:
|
||||
// A mode that specifies which part of the system is currently in control.
|
||||
// The mode can be used to limit certain features, like setting time, navigation
|
||||
@@ -101,6 +104,8 @@ public:
|
||||
std::vector<std::byte> encode();
|
||||
void decode(std::vector<std::byte> data);
|
||||
|
||||
properties::Property::Visibility visibility() const;
|
||||
bool showHiddenSceneGraphNodes() const;
|
||||
void toggleShutdownMode();
|
||||
|
||||
Mode currentMode() const;
|
||||
@@ -136,6 +141,8 @@ private:
|
||||
void resetPropertyChangeFlagsOfSubowners(openspace::properties::PropertyOwner* po);
|
||||
|
||||
properties::BoolProperty _printEvents;
|
||||
properties::OptionProperty _visibility;
|
||||
properties::BoolProperty _showHiddenSceneGraphNodes;
|
||||
|
||||
std::unique_ptr<Scene> _scene;
|
||||
std::unique_ptr<AssetManager> _assetManager;
|
||||
|
||||
@@ -62,13 +62,15 @@ class Property {
|
||||
public:
|
||||
/**
|
||||
* The visibility classes for Property%s. The classes are strictly ordered as
|
||||
* All > Developer > User > Hidden
|
||||
* Hidden > Developer > AdvancedUser > User > NoviceUser > Always
|
||||
*/
|
||||
enum class Visibility {
|
||||
Hidden = 3, ///< Never visible
|
||||
Developer = 2, ///< Visible in Developer mode
|
||||
User = 1, ///< Visible in User mode
|
||||
All = 0, ///< Visible for all types, no matter what
|
||||
Hidden = 5, ///< Never visible
|
||||
Developer = 4, ///< Visible in Developer mode
|
||||
AdvancedUser = 3, ///< Visible in Advanced User mode
|
||||
User = 2, ///< Visible in User mode
|
||||
NoviceUser = 1, ///< Visible in Novice User mode
|
||||
Always = 0, ///< Visible for all types, no matter what
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,7 +101,7 @@ public:
|
||||
/// The user facing description of this Property
|
||||
const char* description;
|
||||
/// Determines the visibility of this Property in the user interface
|
||||
Visibility visibility = Visibility::All;
|
||||
Visibility visibility = Visibility::Always;
|
||||
};
|
||||
|
||||
/// An OnChangeHandle is returned by the onChange method to uniquely identify an
|
||||
@@ -119,7 +121,7 @@ public:
|
||||
* necessary information for this Property. #PropertyInfo::identifier needs to be
|
||||
* unique for each PropertyOwner. The #PropertyInfo::guiName will be stored in the
|
||||
* metaData to be accessed by the GUI elements using the #PropertyInfo::guiName key.
|
||||
* The default visibility settings is Visibility::All, whereas the default read-only
|
||||
* The default visibility settings is Visibility::Always, whereas the default read-only
|
||||
* state is \c false.
|
||||
*
|
||||
* \param info The PropertyInfo structure that contains all the required static
|
||||
|
||||
@@ -168,11 +168,6 @@ DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
|
||||
{ Type::Focus, "Focus" },
|
||||
{ Type::Camera, "Camera" }
|
||||
});
|
||||
_source.type.onChange([this]() {
|
||||
_source.nodeName.setVisibility(
|
||||
properties::Property::Visibility(_source.type == Type::Node)
|
||||
);
|
||||
});
|
||||
if (p.sourceType.has_value()) {
|
||||
_source.type = codegen::map<Type>(*p.sourceType);
|
||||
}
|
||||
@@ -201,11 +196,6 @@ DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
|
||||
{ Type::Focus, "Focus" },
|
||||
{ Type::Camera, "Camera" }
|
||||
});
|
||||
_reference.type.onChange([this]() {
|
||||
_reference.nodeName.setVisibility(
|
||||
properties::Property::Visibility(_reference.type == Type::Node)
|
||||
);
|
||||
});
|
||||
_reference.type = codegen::map<Type>(p.referenceType);
|
||||
addProperty(_reference.type);
|
||||
|
||||
@@ -228,11 +218,6 @@ DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
|
||||
{ Type::Focus, "Focus" },
|
||||
{ Type::Camera, "Camera" }
|
||||
});
|
||||
_destination.type.onChange([this]() {
|
||||
_destination.nodeName.setVisibility(
|
||||
properties::Property::Visibility(_source.type == Type::Node)
|
||||
);
|
||||
});
|
||||
if (p.destinationType.has_value()) {
|
||||
_destination.type = codegen::map<Type>(*p.destinationType);
|
||||
}
|
||||
|
||||
@@ -490,18 +490,6 @@ void ImGUIModule::renderFrame(float deltaTime, const glm::vec2& windowSize,
|
||||
comp->setEnabled(enabled);
|
||||
}
|
||||
|
||||
// Render and Update property visibility
|
||||
// Fragile! Keep this in sync with properties::Property::Visibility
|
||||
using V = properties::Property::Visibility;
|
||||
int t = static_cast<std::underlying_type_t<V>>(_currentVisibility);
|
||||
|
||||
// Array is sorted by importance
|
||||
std::array<const char*, 4> items = { "User", "Developer", "Hidden", "All" };
|
||||
ImGui::Combo("PropertyVisibility", &t, items.data(), static_cast<int>(items.size()));
|
||||
|
||||
_currentVisibility = static_cast<V>(t);
|
||||
_property.setVisibility(_currentVisibility);
|
||||
|
||||
#ifdef SHOW_IMGUI_HELPERS
|
||||
ImGui::Checkbox("ImGUI Internals", &_showInternals);
|
||||
if (_showInternals) {
|
||||
|
||||
@@ -124,9 +124,6 @@ private:
|
||||
UniformCache(tex, ortho) _uniformCache;
|
||||
std::unique_ptr<ghoul::opengl::Texture> _fontTexture;
|
||||
|
||||
properties::Property::Visibility _currentVisibility =
|
||||
properties::Property::Visibility::Developer;
|
||||
|
||||
std::vector<ImGuiContext*> _contexts;
|
||||
|
||||
std::vector<TouchInput> _validTouchStates;
|
||||
|
||||
@@ -52,22 +52,17 @@ public:
|
||||
void setPropertyOwnerFunction(
|
||||
std::function<std::vector<properties::PropertyOwner*>()> func);
|
||||
|
||||
void setVisibility(properties::Property::Visibility visibility);
|
||||
|
||||
void render() override;
|
||||
|
||||
protected:
|
||||
void renderPropertyOwner(properties::PropertyOwner* owner);
|
||||
void renderProperty(properties::Property* prop, properties::PropertyOwner* owner);
|
||||
|
||||
properties::Property::Visibility _visibility = properties::Property::Visibility::User;
|
||||
|
||||
std::vector<properties::PropertyOwner*> _propertyOwners;
|
||||
std::function<std::vector<properties::PropertyOwner*>()> _propertyOwnerFunction;
|
||||
|
||||
properties::BoolProperty _useTreeLayout;
|
||||
properties::StringListProperty _treeOrdering;
|
||||
properties::BoolProperty _ignoreHiddenHint;
|
||||
};
|
||||
|
||||
} // namespace openspace::gui
|
||||
|
||||
@@ -42,7 +42,6 @@ namespace openspace::gui {
|
||||
GuiParallelComponent::GuiParallelComponent()
|
||||
: GuiPropertyComponent("Parallel", "Parallel Connection")
|
||||
{
|
||||
setVisibility(properties::Property::Visibility::All);
|
||||
}
|
||||
|
||||
void GuiParallelComponent::renderDisconnected() {
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#include <modules/imgui/include/imgui_include.h>
|
||||
#include <modules/imgui/include/renderproperties.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <ghoul/misc/misc.h>
|
||||
#include <algorithm>
|
||||
@@ -59,15 +61,17 @@ namespace {
|
||||
"the hidden hints are followed."
|
||||
};
|
||||
|
||||
int nVisibleProperties(const std::vector<openspace::properties::Property*>& props,
|
||||
openspace::properties::Property::Visibility visibility)
|
||||
int nVisibleProperties(const std::vector<openspace::properties::Property*>& props)
|
||||
{
|
||||
using Visibility = openspace::properties::Property::Visibility;
|
||||
Visibility visibilityFilter = openspace::global::openSpaceEngine->visibility();
|
||||
|
||||
return static_cast<int>(std::count_if(
|
||||
props.begin(),
|
||||
props.end(),
|
||||
[visibility](openspace::properties::Property* p) {
|
||||
[visibilityFilter](openspace::properties::Property* p) {
|
||||
using V = openspace::properties::Property::Visibility;
|
||||
return static_cast<std::underlying_type_t<V>>(visibility) >=
|
||||
return static_cast<std::underlying_type_t<V>>(visibilityFilter) >=
|
||||
static_cast<std::underlying_type_t<V>>(p->visibility());
|
||||
}
|
||||
));
|
||||
@@ -187,11 +191,9 @@ GuiPropertyComponent::GuiPropertyComponent(std::string identifier, std::string g
|
||||
: GuiComponent(std::move(identifier), std::move(guiName))
|
||||
, _useTreeLayout(UseTreeInfo, useTree)
|
||||
, _treeOrdering(OrderingInfo)
|
||||
, _ignoreHiddenHint(IgnoreHiddenInfo)
|
||||
{
|
||||
addProperty(_useTreeLayout);
|
||||
addProperty(_treeOrdering);
|
||||
addProperty(_ignoreHiddenHint);
|
||||
}
|
||||
|
||||
void GuiPropertyComponent::setPropertyOwners(
|
||||
@@ -206,10 +208,6 @@ void GuiPropertyComponent::setPropertyOwnerFunction(
|
||||
_propertyOwnerFunction = std::move(func);
|
||||
}
|
||||
|
||||
void GuiPropertyComponent::setVisibility(properties::Property::Visibility visibility) {
|
||||
_visibility = visibility;
|
||||
}
|
||||
|
||||
void GuiPropertyComponent::renderPropertyOwner(properties::PropertyOwner* owner) {
|
||||
using namespace properties;
|
||||
|
||||
@@ -217,12 +215,12 @@ void GuiPropertyComponent::renderPropertyOwner(properties::PropertyOwner* owner)
|
||||
return;
|
||||
}
|
||||
|
||||
const int nThisProperty = nVisibleProperties(owner->properties(), _visibility);
|
||||
const int nThisProperty = nVisibleProperties(owner->properties());
|
||||
ImGui::PushID(owner->identifier().c_str());
|
||||
const std::vector<PropertyOwner*>& subOwners = owner->propertySubOwners();
|
||||
for (PropertyOwner* subOwner : subOwners) {
|
||||
const std::vector<Property*>& properties = subOwner->propertiesRecursive();
|
||||
int count = nVisibleProperties(properties, _visibility);
|
||||
int count = nVisibleProperties(properties);
|
||||
if (count == 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -286,6 +284,7 @@ void GuiPropertyComponent::render() {
|
||||
ImGui::SetNextWindowBgAlpha(0.75f);
|
||||
ImGui::Begin(guiName().c_str(), &v);
|
||||
_isEnabled = v;
|
||||
bool showHiddenNode = openspace::global::openSpaceEngine->showHiddenSceneGraphNodes();
|
||||
|
||||
_isCollapsed = ImGui::IsWindowCollapsed();
|
||||
using namespace properties;
|
||||
@@ -373,12 +372,14 @@ void GuiPropertyComponent::render() {
|
||||
dynamic_cast<SceneGraphNode*>(*owners.begin())->guiPath().empty());
|
||||
|
||||
auto renderProp = [&](properties::PropertyOwner* pOwner) {
|
||||
const int count = nVisibleProperties(pOwner->propertiesRecursive(), _visibility);
|
||||
const int count = nVisibleProperties(pOwner->propertiesRecursive());
|
||||
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
auto header = [&]() -> bool {
|
||||
if (owners.size() > 1) {
|
||||
// Create a header in case we have multiple owners
|
||||
@@ -402,7 +403,7 @@ void GuiPropertyComponent::render() {
|
||||
};
|
||||
|
||||
if (!_useTreeLayout || noGuiGroups) {
|
||||
if (!_ignoreHiddenHint) {
|
||||
if (!showHiddenNode) {
|
||||
// Remove all of the nodes that we want hidden first
|
||||
owners.erase(
|
||||
std::remove_if(
|
||||
@@ -424,7 +425,7 @@ void GuiPropertyComponent::render() {
|
||||
for (properties::PropertyOwner* pOwner : owners) {
|
||||
// We checked above that pOwner is a SceneGraphNode
|
||||
SceneGraphNode* nOwner = static_cast<SceneGraphNode*>(pOwner);
|
||||
if (!_ignoreHiddenHint && nOwner->hasGuiHintHidden()) {
|
||||
if (!showHiddenNode && nOwner->hasGuiHintHidden()) {
|
||||
continue;
|
||||
}
|
||||
const std::string gui = nOwner->guiPath();
|
||||
@@ -491,7 +492,9 @@ void GuiPropertyComponent::renderProperty(properties::Property* prop,
|
||||
|
||||
// Check if the visibility of the property is high enough to be displayed
|
||||
using V = properties::Property::Visibility;
|
||||
const auto v = static_cast<std::underlying_type_t<V>>(_visibility);
|
||||
using Visibility = openspace::properties::Property::Visibility;
|
||||
Visibility visibilityFilter = openspace::global::openSpaceEngine->visibility();
|
||||
const auto v = static_cast<std::underlying_type_t<V>>(visibilityFilter);
|
||||
const auto propV = static_cast<std::underlying_type_t<V>>(prop->visibility());
|
||||
if (v >= propV) {
|
||||
auto it = FunctionMapping.find(prop->className());
|
||||
|
||||
@@ -63,7 +63,7 @@ void DataPlane::initializeGL() {
|
||||
// else if autofilter is turned off, register backgroundValues
|
||||
}
|
||||
else {
|
||||
_backgroundValues.setVisibility(properties::Property::Visibility::All);
|
||||
_backgroundValues.setVisibility(properties::Property::Visibility::Always);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ void DataSphere::initializeGL() {
|
||||
// else if autofilter is turned off, register backgroundValues
|
||||
}
|
||||
else {
|
||||
_backgroundValues.setVisibility(properties::Property::Visibility::All);
|
||||
_backgroundValues.setVisibility(properties::Property::Visibility::Always);
|
||||
//_backgroundValues.setVisible(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -134,7 +134,7 @@ void IswaDataGroup::registerProperties() {
|
||||
// else if autofilter is turned off, register backgroundValues
|
||||
}
|
||||
else {
|
||||
_backgroundValues.setVisibility(properties::Property::Visibility::All);
|
||||
_backgroundValues.setVisibility(properties::Property::Visibility::Always);
|
||||
//_backgroundValues.setVisible(true);
|
||||
}
|
||||
ghoul::Dictionary d;
|
||||
|
||||
@@ -151,7 +151,7 @@ void KameleonPlane::initializeGL() {
|
||||
// else if autofilter is turned off, register backgroundValues
|
||||
}
|
||||
else {
|
||||
_backgroundValues.setVisibility(properties::Property::Visibility::All);
|
||||
_backgroundValues.setVisibility(properties::Property::Visibility::Always);
|
||||
//_backgroundValues.setVisible(true);
|
||||
}
|
||||
});
|
||||
|
||||
4
scripts/developer_settings.lua
Normal file
4
scripts/developer_settings.lua
Normal file
@@ -0,0 +1,4 @@
|
||||
if os.getenv("OPENSPACE_DEVELOPER")
|
||||
then
|
||||
openspace.setPropertyValueSingle('OpenSpaceEngine.PropertyVisibility', 4)
|
||||
end
|
||||
@@ -399,6 +399,7 @@ void initialize() {
|
||||
rootPropertyOwner->addPropertySubOwner(global::dashboard);
|
||||
|
||||
rootPropertyOwner->addPropertySubOwner(global::userPropertyOwner);
|
||||
rootPropertyOwner->addPropertySubOwner(global::openSpaceEngine);
|
||||
|
||||
syncEngine->addSyncable(global::scriptEngine);
|
||||
}
|
||||
|
||||
@@ -118,12 +118,25 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
openspace::properties::Property::PropertyInfo PrintEventsInfo = {
|
||||
constexpr openspace::properties::Property::PropertyInfo PrintEventsInfo = {
|
||||
"PrintEvents",
|
||||
"Print Events",
|
||||
"If this is enabled, all events that are propagated through the system are "
|
||||
"printed to the log."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo VisibilityInfo = {
|
||||
"PropertyVisibility",
|
||||
"Property Visibility",
|
||||
"Hides or displays different settings in the GUI depending on how advanced they "
|
||||
"are."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ShowHiddenSceneInfo = {
|
||||
"ShowHiddenSceneGraphNodes",
|
||||
"Show Hidden Scene Graph Nodes",
|
||||
"If checked, hidden scene graph nodes are visible in the UI"
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
@@ -131,7 +144,10 @@ namespace openspace {
|
||||
class Scene;
|
||||
|
||||
OpenSpaceEngine::OpenSpaceEngine()
|
||||
: _printEvents(PrintEventsInfo, false)
|
||||
: properties::PropertyOwner({ "OpenSpaceEngine" })
|
||||
, _printEvents(PrintEventsInfo, false)
|
||||
, _visibility(VisibilityInfo)
|
||||
, _showHiddenSceneGraphNodes(ShowHiddenSceneInfo, false)
|
||||
{
|
||||
FactoryManager::initialize();
|
||||
FactoryManager::ref().addFactory<Renderable>("Renderable");
|
||||
@@ -146,6 +162,19 @@ OpenSpaceEngine::OpenSpaceEngine()
|
||||
|
||||
SpiceManager::initialize();
|
||||
TransformationManager::initialize();
|
||||
|
||||
addProperty(_printEvents);
|
||||
addProperty(_visibility);
|
||||
addProperty(_showHiddenSceneGraphNodes);
|
||||
|
||||
using Visibility = openspace::properties::Property::Visibility;
|
||||
_visibility.addOptions({
|
||||
{ static_cast<int>(Visibility::NoviceUser), "Novice User" },
|
||||
{ static_cast<int>(Visibility::User), "User" },
|
||||
{ static_cast<int>(Visibility::AdvancedUser), "Advanced User" },
|
||||
{ static_cast<int>(Visibility::Developer), "Developer" },
|
||||
{ static_cast<int>(Visibility::Hidden), "Everything" },
|
||||
});
|
||||
}
|
||||
|
||||
OpenSpaceEngine::~OpenSpaceEngine() {} // NOLINT
|
||||
@@ -1192,6 +1221,8 @@ void OpenSpaceEngine::preSynchronization() {
|
||||
if (_isRenderingFirstFrame) {
|
||||
setCameraFromProfile(*global::profile);
|
||||
setAdditionalScriptsFromProfile(*global::profile);
|
||||
|
||||
global::scriptEngine->runScriptFile(absPath("${SCRIPTS}/developer_settings.lua"));
|
||||
}
|
||||
|
||||
// Handle callback(s) for change in engine mode
|
||||
@@ -1582,6 +1613,14 @@ void OpenSpaceEngine::decode(std::vector<std::byte> data) {
|
||||
global::syncEngine->decodeSyncables(std::move(data));
|
||||
}
|
||||
|
||||
properties::Property::Visibility openspace::OpenSpaceEngine::visibility() const {
|
||||
return static_cast<properties::Property::Visibility>(_visibility.value());
|
||||
}
|
||||
|
||||
bool openspace::OpenSpaceEngine::showHiddenSceneGraphNodes() const {
|
||||
return _showHiddenSceneGraphNodes;
|
||||
}
|
||||
|
||||
void OpenSpaceEngine::toggleShutdownMode() {
|
||||
if (_shutdown.inShutdown) {
|
||||
// If we are already in shutdown mode, we want to disable it
|
||||
|
||||
@@ -72,6 +72,8 @@ void OptionProperty::addOption(int value, std::string desc) {
|
||||
}
|
||||
}
|
||||
_options.push_back(std::move(option));
|
||||
// Set default value to option added first
|
||||
NumericalProperty::setValue(_options[0].value);
|
||||
}
|
||||
|
||||
void OptionProperty::addOptions(std::vector<std::pair<int, std::string>> options) {
|
||||
@@ -100,7 +102,7 @@ void OptionProperty::setValue(int value) {
|
||||
// @TODO(abock): This should be setValue(value) instead or otherwise the
|
||||
// stored indices and option values start to drift if the
|
||||
// operator T of the OptionProperty is used
|
||||
NumericalProperty::setValue(static_cast<int>(i));
|
||||
NumericalProperty::setValue(static_cast<int>(value));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -110,12 +112,26 @@ void OptionProperty::setValue(int value) {
|
||||
}
|
||||
|
||||
bool OptionProperty::hasOption() const {
|
||||
return value() >= 0 && value() < static_cast<int>(_options.size());
|
||||
auto it = std::find_if(
|
||||
_options.begin(),
|
||||
_options.end(),
|
||||
[setValue = value()](const Option& option) {
|
||||
return option.value == setValue;
|
||||
}
|
||||
);
|
||||
return it !=_options.end();
|
||||
}
|
||||
|
||||
|
||||
const OptionProperty::Option& OptionProperty::option() const {
|
||||
return _options[value()];
|
||||
auto it = std::find_if(
|
||||
_options.begin(),
|
||||
_options.end(),
|
||||
[setValue = value()](const Option& option) {
|
||||
return option.value == setValue;
|
||||
}
|
||||
);
|
||||
return *it;
|
||||
}
|
||||
|
||||
std::string OptionProperty::getDescriptionByValue(int value) {
|
||||
|
||||
@@ -339,9 +339,11 @@ std::string Property::generateBaseJsonDescription() const {
|
||||
|
||||
std::string Property::generateMetaDataJsonDescription() const {
|
||||
static const std::map<Visibility, std::string> VisibilityConverter = {
|
||||
{ Visibility::All, "All" },
|
||||
{ Visibility::Developer, "Developer" },
|
||||
{ Visibility::Always, "Always" },
|
||||
{ Visibility::NoviceUser, "NoviceUser" },
|
||||
{ Visibility::User, "User" },
|
||||
{ Visibility::AdvancedUser, "AdvancedUser" },
|
||||
{ Visibility::Developer, "Developer" },
|
||||
{ Visibility::Hidden, "Hidden" }
|
||||
};
|
||||
Visibility visibility = static_cast<Visibility>(
|
||||
|
||||
Reference in New Issue
Block a user