Fixes for position: sticky, absolute and fixed.

This commit is contained in:
Martín Lucas Golini
2026-05-02 19:41:10 -03:00
parent 89329123fb
commit fcb2cca844
11 changed files with 1302 additions and 58 deletions

View File

@@ -0,0 +1,621 @@
@import url(https://fonts.googleapis.com/css?family=Lato);
@import "../anim.css";
a:active,
a:focus {
outline: none;
}
.no_selection {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.blue_links a {
text-decoration: none;
color: #4183C4;
}
.blue_links a:hover {
text-decoration: underline;
}
body {
font-family: 'Lato', Helvetica, sans-serif;
font-size: 16px;
background-color: #ffffff;
margin: 0;
padding: 0;
overflow-x: hidden;
}
::-moz-selection {
background-color: #d00000;
color: #fff;
}
::selection {
background-color: #d00000;
color: #fff;
}
input,
button,
textarea {
font-family: inherit;
}
#bar.smallscreen {
position: absolute !important;
width: 200px !important;
}
#bar {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
position: fixed;
top: 0px;
bottom: 0px;
width: 250px;
border-right: 1px solid #e0e0e0;
}
#bar #logo {
margin: 40px 0px 0px 50px;
height: 144px;
width: 100px;
background: url('logo.png') no-repeat center;
}
#bar #logo #logo-fx {
position: absolute;
top: -27px;
left: -14px;
height: 225px;
width: 225px;
background: url('fx.png') no-repeat center;
opacity: 0;
-ms-filter: "alpha(opacity=0)";
-khtml-opacity: 0;
-webkit-animation-name: contract;
-moz-animation-name: contract;
-webkit-animation-duration: 6s;
-moz-animation-duration: 6s;
-webkit-animation-iteration-count: infinite;
-moz-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-moz-animation-timing-function: linear;
}
#bar #links li {
margin: 40px 30px 0px 0px;
list-style-type: none;
}
#bar #links li a {
padding: 10px 20px 10px 20px;
text-decoration: none;
color: #808080;
}
#bar #links li a:hover {
color: red;
}
#bar #rss {
margin-top: 100px;
margin-left: 20px;
}
#bar #rss a {
background: url("rss.png") no-repeat 8px center;
padding: 5px 10px 5px 24px;
text-decoration: none;
font-size: 12px;
border-radius: 4px;
-khtml-border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
behavior: url('https://ensoft.dev/assets/js/PIE.php');
background-color: #d0d0d0;
color: white;
}
#bar #rss a:hover {
background-color: red;
}
#content.smallscreen {
left: 200px !important;
}
#content {
position: absolute;
left: 250px;
right: 0;
top: 0;
padding: 0px;
}
#content >h1 {
margin: 50px 0 0 50px;
font-size: 35px;
font-family: 'Lato';
font-weight: 900;
padding: 0;
}
a img {
border: 0;
}
#ajax-target {
position: relative;
}
.loading_corner {
position: fixed;
bottom: 90px;
right: 0px;
width: 32px;
height: 32px;
background: url("loading.gif?v=3") no-repeat !important;
}
.kajax-loading .kajax-loading-image {
width: 32px;
height: 32px;
background: url("loading.gif?v=3") no-repeat center white;
}
#content > .kajax-loading {
position: fixed;
top: 10px;
left: 260px;
}
.stylish-form > .kajax-loading {
position: absolute;
bottom: 45px;
right: 190px;
}
.blend_input {
display: block;
overflow: hidden;
outline: none;
resize: none;
background: transparent;
border: none;
}
.admin-editor {
width: 800px;
margin-left: 100px;
position: relative;
}
.admin-editor .title {
color: #808080;
width: 800px;
font-size: 40px;
height: 40px;
display: block;
overflow: hidden;
outline: none;
resize: none;
background: transparent;
border: none;
}
.admin-editor .body {
color: #808080;
padding-left: 60px;
margin-left: -50px;
background: url('markdown.png') no-repeat top left !important;
margin-top: 40px;
display: block;
overflow: hidden;
outline: none;
resize: none;
background: transparent;
border: none;
width: 800px;
height: 600px;
margin-bottom: 200px;
white-space: pre-wrap;
word-wrap: break-word;
font-size: inherit;
}
.square-link {
display: block;
float: left;
padding: 15px;
margin: 14px 5px 14px 0;
font-weight: bold;
text-decoration: none;
color: #ffffff;
border: none;
cursor: pointer;
}
.square-button {
display: block;
float: left;
padding: 15px;
margin: 14px 5px 14px 0;
font-weight: bold;
text-decoration: none;
color: #ffffff;
border: none;
cursor: pointer;
background-color: black;
border-radius: 5px;
-khtml-border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
behavior: url('https://ensoft.dev/assets/js/PIE.php');
font-size: 16px;
color: white !important;
}
.square-button:hover {
background-color: red;
}
#admin-bar {
position: fixed;
bottom: 0px;
left: 0px;
height: 76px;
width: 100%;
background-color: #202020;
z-index: 2;
}
#admin-bar .container {
width: 800px;
margin: 0 auto;
}
#admin-bar .container .left {
float: left;
}
#admin-bar .container .right {
float: right;
}
#admin-bar .container span {
display: block;
float: left;
padding: 15px;
margin: 14px 5px 14px 0;
font-weight: bold;
text-decoration: none;
color: #ffffff;
border: none;
cursor: pointer;
}
#admin-bar .container a,
#admin-bar .container input[type=submit] {
display: block;
float: left;
padding: 15px;
margin: 14px 5px 14px 0;
font-weight: bold;
text-decoration: none;
color: #ffffff;
border: none;
cursor: pointer;
background-color: black;
border-radius: 5px;
-khtml-border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
behavior: url('https://ensoft.dev/assets/js/PIE.php');
font-size: 16px;
color: white !important;
}
#admin-bar .container a:hover,
#admin-bar .container input[type=submit]:hover {
background-color: red;
}
#admin-posts {
width: 900px;
margin: 0 auto;
}
#admin-posts h1 {
float: left;
}
#admin-posts .button {
float: right !important;
padding: 10px;
font-size: 12px;
margin-top: 26px;
}
#admin-posts ul {
padding: 0;
margin: 0;
clear: both;
}
#admin-posts li {
position: relative;
list-style-type: none;
padding: 18px 0 20px 0;
border-bottom: 1px solid #e0e0e0;
}
#admin-posts li a {
color: inherit;
font-weight: bold;
text-decoration: none;
}
#admin-posts li a:hover {
color: red;
}
#admin-posts li span {
float: right;
display: none;
}
#admin-posts li span a {
color: #a0a0a0;
margin-left: 10px;
font-size: 12px;
}
#admin-posts li em {
position: absolute;
right: 0px;
bottom: 0px;
font-size: 8px;
color: #a0a0a0;
}
#admin-posts .post_block {
float: left;
width: 400px;
padding: 20px;
}
#admin-posts #drafts {
float: left;
width: 400px;
padding: 20px;
border: 4px solid black;
border-top: none;
}
#admin-posts #published {
float: left;
width: 400px;
padding: 20px;
color: #a0a0a0;
}
#admin-posts #logout {
font-weight: bold;
float: right;
clear: both;
font-size: 14px;
margin: 5px 36px -50px 0;
}
#admin-posts #logout a {
text-decoration: none;
color: #4183C4;
}
#admin-posts #logout a:hover {
text-decoration: underline;
}
#admin-posts #logout a {
color: #505050 !important;
}
.big_title {
margin: 50px 0 0 50px;
font-size: 35px;
font-family: 'Lato';
font-weight: 900;
padding: 0;
}
.blog_post {
border-bottom: 1px solid #e0e0e0;
}
.blog_post .date {
border-bottom: 1px solid #e0e0e0;
padding: 25px 0 25px 50px;
font-size: 12px;
font-weight: bold;
}
.blog_post >h1 {
margin: 50px 0 0 50px;
font-size: 35px;
font-family: 'Lato';
font-weight: 900;
padding: 0;
}
.blog_post >h1 >a {
color: black;
text-decoration: none;
}
.blog_post >h1 >a:hover {
color: red;
}
.blog_post .markdown {
width: 500px;
margin: 30px 0 65px 50px;
color: #808080;
}
.blog_post .markdown h1,
.blog_post .markdown h2,
.blog_post .markdown h3,
.blog_post .markdown h4,
.blog_post .markdown h5 {
color: black;
}
.blog_post .markdown li {
line-height: 2.0em;
}
.blog_post .markdown a {
text-decoration: none;
color: #4183C4;
}
.blog_post .markdown a:hover {
text-decoration: underline;
}
.blog_post .markdown img {
border: 5px solid #f0f0f0;
padding: 10px;
border-radius: 10px;
-khtml-border-radius: 10px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
behavior: url('https://ensoft.dev/assets/js/PIE.php');
}
.blog_post .markdown blockquote {
border-left: 5px solid #505050;
padding-left: 16px;
margin: 0;
}
.blog_post .markdown .footnotes {
margin-top: 50px;
font-size: 14px;
}
.blog_post .markdown .footnotes hr {
color: #e0e0e0;
background-color: #e0e0e0;
border: none;
height: 1px;
}
.padded {
padding: 50px 0 0 50px;
}
.styled-box {
border: 5px solid black;
width: 440px;
margin: 50px 0 0 50px;
padding: 0px;
}
.stylish-form {
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.stylish-form .mail_icon {
background: url('mail.png') no-repeat center right;
}
.stylish-form .robot_icon {
background: url('robot.png') no-repeat center right;
}
.stylish-form p {
color: #808080;
font-size: 18px;
}
.stylish-form li {
list-style-type: none;
border-bottom: 1px solid #e0e0e0;
padding: 20px;
margin: 0;
}
.stylish-form input[type=password],
.stylish-form input[type=text],
.stylish-form textarea {
font-size: 22px;
width: 100%;
display: block;
overflow: hidden;
outline: none;
resize: none;
background: transparent;
border: none;
padding-left: 10px;
}
.stylish-form input[type=password]:focus,
.stylish-form input[type=text]:focus,
.stylish-form textarea:focus {
border-left: 4px solid red;
}
.stylish-form input[type=submit] {
display: block;
float: left;
padding: 15px;
margin: 14px 5px 14px 0;
font-weight: bold;
text-decoration: none;
color: #ffffff;
border: none;
cursor: pointer;
background-color: black;
border-radius: 5px;
-khtml-border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
behavior: url('https://ensoft.dev/assets/js/PIE.php');
font-size: 16px;
color: white !important;
float: none;
}
.stylish-form input[type=submit]:hover {
background-color: red;
}
#check-mail-target {
display: none;
text-align: right;
font-style: italic;
margin: 0;
}
#check-mail-target a {
text-decoration: none;
color: #4183C4;
}
#check-mail-target a:hover {
text-decoration: underline;
}
.placeholder {
color: #808080;
}
.form-error {
padding: 5px !important;
display: none;
background-color: #ffffb4;
}
.form-error p {
text-align: center;
display: none;
}
.form-error p:first-child {
display: block !important;
}
#save-success-msg {
position: fixed;
bottom: 0px;
right: 10px;
height: 96px;
width: 100px;
text-align: center;
display: none;
padding: 5px !important;
color: #FFFFFF;
font-weight: bold;
background-color: red;
border-radius: 5px;
-khtml-border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
behavior: url('https://ensoft.dev/assets/js/PIE.php');
}
#minimap {
display: none;
opacity: 0.5;
position: fixed;
top: 0px;
bottom: 0px;
right: 0px;
width: 120px;
list-style-type: none;
padding: 0;
padding: 20px 0 20px 0;
margin: 0;
border-right: 1px solid #e0e0e0;
}
#minimap li .mark {
position: absolute;
bottom: 0;
top: 0;
right: 0;
width: 10px;
background-color: #c00000;
display: none;
}
#minimap li .selected {
background-color: #606060 !important;
}
#minimap li a {
position: relative;
margin-top: 5px;
background-color: #b0b0b0;
border-right: 0;
display: block;
padding: 5px;
padding-right: 10px;
text-align: center;
color: #a0a0a0;
text-decoration: none;
font-size: 12px;
font-weight: bold;
color: white;
-moz-border-radius-topleft: 4px;
-moz-border-radius-bottomleft: 4px;
-webkit-border-top-left-radius: 4px;
-webkit-border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
#minimap li a:hover {
background-color: #808080;
}

View File

@@ -0,0 +1,286 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" xmlns:fb="http://www.facebook.com/2008/fbml"><head>
<title>ensoft</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="shortcut icon" href="https://ensoft.dev/assets/images/favicon.ico">
<link rel="icon" type="image/x-icon" href="https://ensoft.dev/assets/images/favicon.ico">
<link rel="icon" type="image/png" href="https://ensoft.dev/assets/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://ensoft.dev/assets/images/favicon-48x48.png" sizes="48x48">
<link rel="icon" type="image/png" href="https://ensoft.dev/assets/images/favicon-64x64.png" sizes="64x64">
<link rel="alternate" type="application/rss+xml" title="ensoft RSS" href="https://ensoft.dev/rss">
<link rel="stylesheet" type="text/css" href="ensoft.css">
</head>
<body>
<div id="content">
<div class="blog_post" id="pampa-energa-en-tecnpolis">
<h1>
<a name="pampa-energa-en-tecnpolis" href="https://ensoft.dev/blog/pampa-energa-en-tecnpolis">
Pampa Energía en Tecnópolis </a>
</h1>
<div class="markdown"><p>Tuvimos el placer de desarrollar en conjunto con el equipo de <a target="_blank" href="http://www.redkatana.com/es/">Red Katana</a> uno de los juegos principales para el stand de <a target="_blank" href="http://www.pampaenergia.com/">Pampa Energía</a> en su stand de Tecnópolis.</p>
<p>El slogan de este año es "Energía para Transformar" y justamente este
es el punto central de este juego, concientizar a los jugadores acerca
de la importancia de ahorrar energía.</p>
<p><img src="" alt=""></p>
<p>El juego consiste de varias habitaciones de una casa, donde los
jugadores deben encontrar de que manera los personajes están
desperdiciando la energía y ayudarlos a ahorrarla.</p>
<p><img src="" alt=""></p>
<p>Nuestra labor consistió en el desarrollo completo del juego, para ser utilizado en las pantallas táctiles de dicho stand.</p>
<p>Stand de pampa energía en tecnópolis:</p>
<p><img src="" alt=""></p>
<p>Visitantes jugando:</p>
<p><img src="" alt=""></p>
</div>
</div>
<div class="blog_post" id="tia-maria-get-behind-the-mask">
<h1>
<a name="tia-maria-get-behind-the-mask" href="https://ensoft.dev/blog/tia-maria-get-behind-the-mask">
Tia Maria: Get Behind The Mask </a>
</h1>
<div class="markdown"><p>Bajo encargo del estudio de diseño <a target="_blank" href="http://www.fpestudio.com/">Four Players Studio</a> desarrollamos para la renombrada marca <a target="_blank" href="http://www.tiamaria.com/">Tia Maria</a> una aplicación para celulares y tablets Android que se utilizaría para presentar la nueva campaña publicitaria de la marca:</p>
<p><img src="" alt=""></p>
<p>El software consiste en una serie de videos publicitarios por un lado
y por el otro la funcionalidad de escanear código QR, dentro de los
cuales reconoce los códigos ganadores e informa los premios a los
participantes.</p>
<p><img src="" alt=""></p>
</div>
</div>
<div class="blog_post" id="entropia-engine-">
<h1>
<a name="entropia-engine-" href="https://ensoft.dev/blog/entropia-engine-">
Entropia Engine++ </a>
</h1>
<div class="markdown"><h2>Características</h2>
<p><a target="_blank" href="http://www.eepp.com.ar/">Entropia Engine++</a>,
es un motor para el desarrollo de videojuegos 2D multiplataforma,
creado para facilitar la creación y distribución de los mismos, pensado
para un rendimiento óptimo en cualquier plataforma en la que sea
utilizado.</p>
<h2>Portabilidad</h2>
<p>El código del mismo puede ser compilado para una cantidad sustancial
de plataformas, de diferente naturaleza sin modificar el código. Por
ejemplo el mismo juego, puede ser compilado para Windows, Linux, Mac OS,
iOS (iPhone y iPad) y Android (Tablets y SmartPhones).</p>
<h2>Diferencias</h2>
<p>Se diferencia principalmente de otros motores de desarrollo de
videojuegos 2D comerciales y código abierto es en la facilidad de
portabilidad del mismo gracias a su modularidad y extensibilidad, provee
integramente más módulos de los que ofrecen otras alternativas, entre
ellos el módulo de interfaz gráfica, que reduce los tiempos de
desarrollo en gran medida. Por otro lado, nuestra elección fue trabajar
con un lenguaje compilado y nativo para las plataformas, para poder
aprovechar al máximo el hardware de cada una de estas, pudiendo tener
control total del manejo de la memoria, y teniendo un rendimiento óptimo
en todas las plataformas.</p>
<h2>Desarrollo</h2>
<p>El motor se encuentra en activo desarrollo y mantenido por uno de los
miembros del equipo, se planifica expandir el mismo de acuerdo a las
necesidades que surjan para el desarrollo del videojuego ( entre ellas,
completar el portado del mismo a las plataformas móbiles, módulo de
networking, módulo de scripting ).</p>
<h2>Licencia</h2>
<p>El motor es de código abierto, bajo la licencia MIT<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>, lo que permite utilizarlo libremente tanto para aplicaciones open source como para aplicaciones comerciales.</p>
<h2>Imágenes</h2>
<p>Demo de Interfaz Gráfica
<a target="_blank" href="https://ensoft.dev/assets/blog/eepp-ui.png" title="Demo de Interfaz Gráfica"><img src="" alt="Demo de Interfaz Gráfica"></a></p>
<p>Editor de Mapas
<a target="_blank" href="https://ensoft.dev/assets/blog/eepp-mapeditor.png" title="Editor de Mapas"><img src="" alt="Editor de Mapas"></a></p>
<p>Corriendo en HaikuOS<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup>
<a target="_blank" href="https://ensoft.dev/assets/blog/eepp-haikuos.png" title="Corriendo en HaikuOS"><img src="" alt="Corriendo en HaikuOS"></a>&nbsp;</p>
<h2>Links</h2>
<p><a target="_blank" href="http://code.google.com/p/eepp/">Repositório en Google Code</a></p>
<p><a target="_blank" href="https://ensoft.dev/blog/especificaciones-de-entropia-engine-">Especificaciones Técnicas</a></p>
<div class="footnotes">
<hr>
<ol>
<li id="fn:1">
<p><a target="_blank" href="http://es.wikipedia.org/wiki/MIT_License">Licencia MIT en Wikipedia</a>&nbsp;<a href="#fnref:1" rev="footnote"></a></p>
</li>
<li id="fn:2">
<p><a target="_blank" href="http://haiku-os.org/">HaikuOS</a>&nbsp;<a href="#fnref:2" rev="footnote"></a></p>
</li>
</ol>
</div>
</div>
</div>
<div class="blog_post" id="red-domo">
<h1>
<a name="red-domo" href="https://ensoft.dev/blog/red-domo">
Red Domo </a>
</h1>
<div class="markdown"><p>Tuvimos el placer de trabajar con la gente de <a target="_blank" href="http://deitres.com.ar/">Deitres</a>,
una empresa Marplatense de alcance nacional que utiliza tecnología
inalámbrica de vanguardia en sístemas de monitoreo de alarmas.</p>
<p>El trabajo realizado consiste en un ambicioso proyecto que permite
que su sistema de alarmas esté completamente integrado y pueda ser
accedido vía web, utilizando un navegador web estándar.</p>
<p>Fué todo un desafío para nosotros debido a la infraestuctura que ya
tenía en funcionamiento la empresa y las características de seguridad
que requiere un sistema con estas características.</p>
<p>Después de muchas reuniones, y de un planeamiento cuidadoso comenzamos a desarrollar lo que hoy es <a target="_blank" href="https://www.reddomo.com.ar/">DOMO</a>.</p>
<p><img src="" alt="Domo"></p>
<h2>Naturaleza del Proyecto</h2>
<p>El proyecto fué dividido en tres partes<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>:</p>
<h3>Receptora</h3>
<p>La receptora es la encargarda de recibir todos los datos que llegan de los diferentes sistemas de alarmas.
El desarrollo se llevó a cabo usando <em>C#</em> y partiendo de base de una librería interna proveída por el departamento técnico de Deitres.
Fué planeada con tolerancia a fallos y problemas de conexión.
Ésta luego de procesar los paquetes los envia por un canal seguro hacia el servidor.</p>
<h3>Servidor</h3>
<p>El servidor fué desarrollado integramente en <em>PHP</em>, utilizando un <em>Servidor Dedicado</em>.
Su principal función es la de recibir los datos que envía la receptora,
clasificarlos de forma correspondiente y asociarlos a las cuentas de
usuario y diferentes proveedoras de alarmas distribuidas por todo el
país.</p>
<h3>Página Web.</h3>
<p>La página Web, a su vez está dividida en tres partes:</p>
<ul>
<li>Cuenta de Administrador.</li>
<li>Cuentas para proveedores de servicios de alarmas.</li>
<li>Cuentas para usuarios finales.</li>
</ul>
<p>Fué desarrollada<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup> utilizando <em>PHP</em> y <em>Javascript</em>, haciendo uso intensivo de el servicio de <em>Google Maps</em> para visualizar en tiempo real el estado de todos los equipos de alarmas conectados al sistema.</p>
<p>Toda las transacciones entre el navegador y el servidor son realizadas a través de una conexión segura utilizando el protocolo <strong>SSL</strong>..</p>
<h2>Funcionalidad</h2>
<p>El sistema completo provee una integración total de los servicios ya
ofrecidos por la empresa, mostrando gráficos en tiempo real del
rendimiento de los equipos. Ubicando en un mapa todos los puntos donde
se encuentran las alarmas y permitiendo administrar las cuentas de
usuario y las alertas recibidas.</p>
<p><img src="" alt="Domo"></p>
<p>Además para el usuario final le permite acceder, cualquiera sea su
ubicación al estado de las diferentes alarmas que posee intaladas,
permitiendole ver el estado y los mensajes que ha enviado la alarma al
sistema.</p>
<p>Cabe destacar también que los usuarios que tienen contratado el servicio de <strong>domótica</strong>
pueden controlar los diferentes dispositivos conectados a las alarmas,
por ejemplo, pueden abrir y cerrar puertas, encender luces controlar
calderas, etc. Todo desde su navegador web.</p>
<h2>Palabras Finales</h2>
<p>La implementación principal del proyecto llevó alrededor de cuatro meses de trabajo con colaboración constante entre las partes.</p>
<p>Hemos formado una sólida relación de negocios con dicha empresa y al
día de hoy continuamos trabajando en agregar funcionalidad al sistema, y
siempre dispuestos a innovar de forma conjunta.</p>
<div class="footnotes">
<hr>
<ol>
<li id="fn:1">
<p>Debido a la discreción requerida en este tipo de proyectos, se mencionan superficialmente las características del mismo.&nbsp;<a href="#fnref:1" rev="footnote"></a></p>
</li>
<li id="fn:2">
<p>El diseño gráfico de la misma fué contratado externamente por Deitres y es una realización de <a target="_blank" href="http://chic-creative.com/">Chic Creative</a>.&nbsp;<a href="#fnref:2" rev="footnote"></a></p>
</li>
</ol>
</div>
</div>
</div>
<div class="blog_post" id="todo-deportes">
<h1>
<a name="todo-deportes" href="https://ensoft.dev/blog/todo-deportes">
Todo Deportes </a>
</h1>
<div class="markdown"><p>La web de <a target="_blank" href="http://www.tododeportesmdq.com.ar/nosotros">Todo Deportes</a> fué diseñada para una empresa que comercializa insumos para <strong>deportes extremos</strong>.</p>
<p>Se mantuvo un estilo simple, en tonos amarillos y negros acorde al logo de la empresa.</p>
<p>El sitio posee una galería interactiva en la parte superior de la
página, una navegación fluida y cuatro secciones: la página de inicio,
las marcas ofrecidas, el team sponsoreado y un formulario de contacto.</p>
<p><img src="" alt="Todo Deportes"></p>
</div>
</div>
</div>
<div id="bar"><a href="https://ensoft.dev/"><div id="logo"><div id="logo-fx"></div></div></a>
<ul id="links">
<li><a class="ajax-link" href="https://ensoft.dev/portfolio">portfolio</a></li>
<li><a class="ajax-link" href="https://ensoft.dev/nosotros">nosotros</a></li>
<!--<li><a class="ajax-link" href="https://ensoft.dev/blog">blog</a></li>-->
<li><a class="ajax-link" href="https://ensoft.dev/contacto">contacto</a></li>
</ul>
<div id="rss"><a href="https://ensoft.dev/rss">RSS</a></div>
</div>
</body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -31,21 +31,27 @@ class EE_API UIHTMLWidget : public UILayout {
virtual void onDisplayChange();
CSSDisplay getDisplay() const { return mDisplay; }
void setDisplay( CSSDisplay display );
CSSPosition getCSSPosition() const { return mPosition; }
void setCSSPosition( CSSPosition position );
CSSFloat getCSSFloat() const { return mFloat; }
void setCSSFloat( CSSFloat cssFloat );
CSSClear getCSSClear() const { return mClear; }
void setCSSClear( CSSClear cssClear );
const Rectf& getOffsets() const { return mOffsets; }
void setOffsets( const Rectf& offsets );
int getZIndex() const { return mZIndex; }
void setZIndex( int zIndex );
virtual std::vector<PropertyId> getPropertiesImplemented() const;
@@ -57,10 +63,18 @@ class EE_API UIHTMLWidget : public UILayout {
virtual void updateLayout();
virtual void onParentChange();
virtual void onPositionChange();
UIWidget* getContainingBlock();
void positionOutOfFlowChildren();
void updateOutOfFlowPosition();
void updateStickyPosition();
virtual RichText* getRichTextPtr() { return nullptr; }
virtual bool isMergeable() const { return false; }
@@ -84,6 +98,14 @@ class EE_API UIHTMLWidget : public UILayout {
int mZIndex{ 0 };
UILayouter* mLayouter{ nullptr };
UnorderedMap<std::string, StyleSheetProperty> mDataProperties;
Uint32 mScrollCb{ 0 };
Node* mScrollTarget{ nullptr };
Vector2f mStickyBasePos;
bool mIsUpdatingScroll{ false };
void updateScrollListeners();
void onScrollTargetPositionChange();
};
}} // namespace EE::UI

View File

@@ -178,6 +178,7 @@ class EE_API UIHTMLBody : public UIRichText {
virtual Uint32 getType() const override;
bool isType( const Uint32& type ) const override;
bool applyProperty( const StyleSheetProperty& attribute ) override;
virtual void updateLayout() override;
protected:
bool mPropagatedBackground{ false };

View File

@@ -2,6 +2,8 @@
#include <eepp/ui/uihtmlwidget.hpp>
#include <eepp/ui/uilayouter.hpp>
#include <eepp/ui/uilayoutermanager.hpp>
#include <eepp/ui/uiscrollablewidget.hpp>
#include <eepp/ui/uiscrollview.hpp>
namespace EE { namespace UI {
@@ -12,6 +14,8 @@ UIHTMLWidget* UIHTMLWidget::New() {
UIHTMLWidget::UIHTMLWidget( const std::string& tag ) : UILayout( tag ) {}
UIHTMLWidget::~UIHTMLWidget() {
if ( mScrollTarget && mScrollCb )
mScrollTarget->removeEventListener( mScrollCb );
eeSAFE_DELETE( mLayouter );
}
@@ -65,6 +69,7 @@ void UIHTMLWidget::setCSSPosition( CSSPosition position ) {
if ( getLayoutWidthPolicy() == SizePolicy::MatchParent )
setLayoutWidthPolicy( SizePolicy::WrapContent );
}
updateScrollListeners();
onPositionChange();
}
}
@@ -201,17 +206,22 @@ void UIHTMLWidget::updateLayout() {
UIWidget* UIHTMLWidget::getContainingBlock() {
if ( mPosition == CSSPosition::Fixed ) {
Node* parent = getParent();
UIWidget* lastWidget = parent && parent->isWidget() ? parent->asType<UIWidget>() : nullptr;
UIWidget* cb = parent && parent->isWidget() ? parent->asType<UIWidget>() : nullptr;
while ( parent ) {
if ( parent->isType( UI_TYPE_SCROLLVIEW ) ) {
cb = parent->asType<UIWidget>();
break;
}
if ( parent->isWidget() )
lastWidget = parent->asType<UIWidget>();
cb = parent->asType<UIWidget>();
parent = parent->getParent();
}
return lastWidget;
return cb;
}
Node* parent = getParent();
UIWidget* lastWidget = nullptr;
UIWidget* htmlWidget = nullptr;
while ( parent ) {
if ( parent->isWidget() ) {
lastWidget = parent->asType<UIWidget>();
@@ -219,11 +229,14 @@ UIWidget* UIHTMLWidget::getContainingBlock() {
if ( lastWidget->asType<UIHTMLWidget>()->getCSSPosition() != CSSPosition::Static ) {
return lastWidget;
}
if ( lastWidget->isType( UI_TYPE_HTML_HTML ) ) {
htmlWidget = lastWidget;
}
}
}
parent = parent->getParent();
}
return lastWidget;
return htmlWidget ? htmlWidget : lastWidget;
}
void UIHTMLWidget::positionOutOfFlowChildren() {
@@ -233,66 +246,182 @@ void UIHTMLWidget::positionOutOfFlowChildren() {
UIHTMLWidget* htmlChild = static_cast<UIHTMLWidget*>( child );
CSSPosition pos = htmlChild->getCSSPosition();
if ( pos == CSSPosition::Absolute || pos == CSSPosition::Fixed ) {
UIWidget* cb = htmlChild->getContainingBlock();
if ( cb ) {
Rectf cbContentOffset = cb->getPixelsContentOffset();
Float cbContentWidth = cb->getPixelsSize().getWidth() - cbContentOffset.Left -
cbContentOffset.Right;
Float cbContentHeight = cb->getPixelsSize().getHeight() - cbContentOffset.Top -
cbContentOffset.Bottom;
Rectf margin = htmlChild->getLayoutPixelsMargin();
Float childWidth = htmlChild->getPixelsSize().getWidth();
Float childHeight = htmlChild->getPixelsSize().getHeight();
Float top = 0;
Float left = 0;
bool useTop = htmlChild->mTopEq != "auto";
bool useBottom = htmlChild->mBottomEq != "auto";
bool useLeft = htmlChild->mLeftEq != "auto";
bool useRight = htmlChild->mRightEq != "auto";
if ( useLeft ) {
left = htmlChild->lengthFromValue(
htmlChild->mLeftEq, CSS::PropertyRelativeTarget::ContainingBlockWidth,
0 );
} else if ( useRight ) {
Float rightVal = htmlChild->lengthFromValue(
htmlChild->mRightEq, CSS::PropertyRelativeTarget::ContainingBlockWidth,
0 );
left = cbContentWidth - childWidth - margin.Left - margin.Right - rightVal;
}
if ( useTop ) {
top = htmlChild->lengthFromValue(
htmlChild->mTopEq, CSS::PropertyRelativeTarget::ContainingBlockHeight,
0 );
} else if ( useBottom ) {
Float bottomVal = htmlChild->lengthFromValue(
htmlChild->mBottomEq,
CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
top =
cbContentHeight - childHeight - margin.Top - margin.Bottom - bottomVal;
}
top += margin.Top;
left += margin.Left;
Vector2f cbPos( cbContentOffset.Left, cbContentOffset.Top );
cbPos.x += left;
cbPos.y += top;
Vector2f worldPos = cb->convertToWorldSpace( cbPos );
Vector2f localPos = convertToNodeSpace( worldPos );
htmlChild->setPixelsPosition( localPos );
}
htmlChild->updateOutOfFlowPosition();
}
}
child = child->getNextNode();
}
}
void UIHTMLWidget::updateOutOfFlowPosition() {
UIWidget* cb = getContainingBlock();
if ( !cb )
return;
Rectf cbContentOffset = cb->getPixelsContentOffset();
Float cbContentWidth =
cb->getPixelsSize().getWidth() - cbContentOffset.Left - cbContentOffset.Right;
Float cbContentHeight =
cb->getPixelsSize().getHeight() - cbContentOffset.Top - cbContentOffset.Bottom;
Rectf margin = getLayoutPixelsMargin();
Float childWidth = getPixelsSize().getWidth();
Float childHeight = getPixelsSize().getHeight();
Float top = 0;
Float left = 0;
bool useTop = mTopEq != "auto";
bool useBottom = mBottomEq != "auto";
bool useLeft = mLeftEq != "auto";
bool useRight = mRightEq != "auto";
if ( useLeft ) {
left = lengthFromValue( mLeftEq, CSS::PropertyRelativeTarget::ContainingBlockWidth, 0 );
} else if ( useRight ) {
Float rightVal =
lengthFromValue( mRightEq, CSS::PropertyRelativeTarget::ContainingBlockWidth, 0 );
left = cbContentWidth - childWidth - margin.Left - margin.Right - rightVal;
}
if ( useTop ) {
top = lengthFromValue( mTopEq, CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
} else if ( useBottom ) {
Float bottomVal =
lengthFromValue( mBottomEq, CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
top = cbContentHeight - childHeight - margin.Top - margin.Bottom - bottomVal;
}
top += margin.Top;
left += margin.Left;
Vector2f cbPos( cbContentOffset.Left, cbContentOffset.Top );
cbPos.x += left;
cbPos.y += top;
Vector2f worldPos = cb->convertToWorldSpace( cbPos );
Vector2f localPos = getParent()->convertToNodeSpace( worldPos );
setPixelsPosition( localPos );
}
void UIHTMLWidget::updateStickyPosition() {
if ( !mScrollTarget )
return;
UIWidget* cb = getContainingBlock();
if ( !cb )
return;
Vector2f baseWorldPos = getParent()->convertToWorldSpace( mStickyBasePos );
Node* viewport = mScrollTarget->getParent();
if ( !viewport )
return;
Vector2f posInViewport = viewport->convertToNodeSpace( baseWorldPos );
Float topOffset = 0;
bool useTop = mTopEq != "auto";
if ( useTop )
topOffset =
lengthFromValue( mTopEq, CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
Float bottomOffset = 0;
bool useBottom = mBottomEq != "auto";
if ( useBottom )
bottomOffset =
lengthFromValue( mBottomEq, CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
Vector2f newPosInViewport = posInViewport;
if ( useTop ) {
if ( posInViewport.y < topOffset ) {
newPosInViewport.y = topOffset;
}
}
if ( useBottom ) {
Float viewportHeight = viewport->getSize().getHeight();
if ( posInViewport.y + getPixelsSize().getHeight() > viewportHeight - bottomOffset ) {
newPosInViewport.y = viewportHeight - bottomOffset - getPixelsSize().getHeight();
}
}
Vector2f cbWorldPos = cb->convertToWorldSpace( Vector2f( 0, 0 ) );
Vector2f cbInViewport = viewport->convertToNodeSpace( cbWorldPos );
Float cbBottomInViewport =
cbInViewport.y + cb->getPixelsSize().getHeight() - cb->getPixelsPadding().Bottom;
if ( newPosInViewport.y + getPixelsSize().getHeight() > cbBottomInViewport ) {
newPosInViewport.y = cbBottomInViewport - getPixelsSize().getHeight();
}
if ( newPosInViewport.y < cbInViewport.y + cb->getPixelsPadding().Top ) {
newPosInViewport.y = cbInViewport.y + cb->getPixelsPadding().Top;
}
if ( newPosInViewport != posInViewport ) {
Vector2f newWorldPos = viewport->convertToWorldSpace( newPosInViewport );
Vector2f newLocalPos = getParent()->convertToNodeSpace( newWorldPos );
mIsUpdatingScroll = true;
setPixelsPosition( newLocalPos );
mIsUpdatingScroll = false;
} else {
mIsUpdatingScroll = true;
setPixelsPosition( mStickyBasePos );
mIsUpdatingScroll = false;
}
}
void UIHTMLWidget::updateScrollListeners() {
if ( mScrollTarget ) {
if ( mScrollCb ) {
mScrollTarget->removeEventListener( mScrollCb );
mScrollCb = 0;
}
mScrollTarget = nullptr;
}
if ( mPosition == CSSPosition::Fixed || mPosition == CSSPosition::Sticky ) {
Node* parent = getParent();
while ( parent ) {
if ( parent->isType( UI_TYPE_SCROLLVIEW ) ) {
mScrollTarget = parent->asType<UIScrollView>()->getScrollView();
break;
}
parent = parent->getParent();
}
if ( mScrollTarget ) {
mScrollCb = mScrollTarget->on( Event::OnPositionChange, [this]( const Event* ) {
onScrollTargetPositionChange();
} );
}
}
}
void UIHTMLWidget::onParentChange() {
UILayout::onParentChange();
updateScrollListeners();
}
void UIHTMLWidget::onPositionChange() {
UILayout::onPositionChange();
if ( mPosition == CSSPosition::Sticky && !mIsUpdatingScroll ) {
mStickyBasePos = getPixelsPosition();
updateStickyPosition();
}
}
void UIHTMLWidget::onScrollTargetPositionChange() {
if ( mPosition == CSSPosition::Fixed ) {
updateOutOfFlowPosition();
} else if ( mPosition == CSSPosition::Sticky ) {
updateStickyPosition();
}
}
void UIHTMLWidget::invalidateIntrinsicSize() {
if ( mLayouter )
mLayouter->invalidateIntrinsicWidths();

View File

@@ -112,6 +112,36 @@ bool UIHTMLBody::applyProperty( const StyleSheetProperty& attribute ) {
return UIRichText::applyProperty( attribute );
}
void UIHTMLBody::updateLayout() {
UIRichText::updateLayout();
if ( mChild && mChild->isWidget() ) {
Float maxH = 0;
Node* child = mChild;
while ( child ) {
if ( child->isWidget() ) {
UIWidget* widget = child->asType<UIWidget>();
bool isFixed = false;
if ( widget->isType( UI_TYPE_HTML_WIDGET ) &&
widget->asType<UIHTMLWidget>()->getCSSPosition() == CSSPosition::Fixed ) {
isFixed = true;
}
if ( !isFixed ) {
Float childH =
widget->getPixelsPosition().y + widget->getPixelsSize().getHeight();
maxH = std::max( maxH, childH );
}
}
child = child->getNextNode();
}
if ( maxH > 0 ) {
Float dpH = PixelDensity::pxToDp( maxH );
if ( dpH != getMinSize().getHeight() )
setMinHeight( dpH );
}
}
}
UIRichText* UIRichText::NewHtml() {
auto* html = UIHTMLHtml::New( "html" );
html->setClipType( ClipType::None );

View File

@@ -8,6 +8,8 @@
#include <eepp/ui/uihtmlwidget.hpp>
#include <eepp/ui/uirichtext.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uiscrollbar.hpp>
#include <eepp/ui/uiscrollview.hpp>
#include <eepp/ui/uithememanager.hpp>
#include <eepp/window/engine.hpp>
#include <eepp/window/window.hpp>
@@ -351,3 +353,88 @@ UTEST( UIHTMLWidget, positionOutOfFlow_RightBottomWithMargin ) {
Engine::destroySingleton();
}
UTEST( UIHTMLWidget, positionOutOfFlow_FixedScroll ) {
init_ui_test();
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
UIScrollView* scrollView = UIScrollView::New();
scrollView->setParent( sceneNode->getRoot() );
scrollView->setPixelsSize( 800, 600 );
UIHTMLBody* body = UIHTMLBody::New( "body" );
body->setParent( scrollView );
body->applyProperty( StyleSheetProperty( "position", "relative" ) );
UIWidget* dummyChild = UIWidget::New();
dummyChild->setParent( body );
dummyChild->setPixelsSize( 800, 2000 );
dummyChild->setPixelsPosition( 0, 0 );
UIHTMLWidget* fixedChild = UIHTMLWidget::New();
fixedChild->setParent( body );
fixedChild->setPixelsSize( 100, 50 );
fixedChild->applyProperty( StyleSheetProperty( "position", "fixed" ) );
fixedChild->applyProperty( StyleSheetProperty( "top", "50px" ) );
fixedChild->applyProperty( StyleSheetProperty( "left", "50px" ) );
sceneNode->updateDirtyLayouts();
// Scroll down by 200px
scrollView->getScrollView()->setPosition( { 0, -200 } );
Vector2f worldPos = fixedChild->convertToWorldSpace( { 0, 0 } );
EXPECT_NEAR( 50.f, worldPos.x, 1.f );
EXPECT_NEAR( 50.f, worldPos.y, 1.f );
Engine::destroySingleton();
}
UTEST( UIHTMLWidget, positionOutOfFlow_StickyScroll ) {
init_ui_test();
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
UIScrollView* scrollView = UIScrollView::New();
scrollView->setParent( sceneNode->getRoot() );
scrollView->setPixelsSize( 800, 600 );
UIHTMLBody* body = UIHTMLBody::New( "body" );
body->setParent( scrollView );
body->applyProperty( StyleSheetProperty( "position", "relative" ) );
UIHTMLWidget* stickyChild = UIHTMLWidget::New();
stickyChild->setParent( body );
stickyChild->setPixelsSize( 100, 50 );
stickyChild->applyProperty( StyleSheetProperty( "margin-top", "100px" ) );
stickyChild->applyProperty( StyleSheetProperty( "position", "sticky" ) );
stickyChild->applyProperty( StyleSheetProperty( "top", "20px" ) );
UIWidget* dummyChild = UIWidget::New();
dummyChild->setParent( body );
dummyChild->setPixelsSize( 800, 2000 );
dummyChild->setPixelsPosition( 0, 0 );
sceneNode->updateDirtyLayouts();
// Force base pos (as if layouter did it)
stickyChild->setPixelsPosition( 0, 100 );
// Ensure base pos was captured correctly
EXPECT_NEAR( 100.f, stickyChild->getPixelsPosition().y, 1.f );
// Scroll down by 50px
Float actualMaxScrollY =
body->getPixelsSize().getHeight() - scrollView->getContainer()->getPixelsSize().getHeight();
scrollView->getVerticalScrollBar()->setValue( 50.f / actualMaxScrollY );
Vector2f worldPos1 = stickyChild->convertToWorldSpace( { 0, 0 } );
EXPECT_NEAR( 50.f, worldPos1.y, 1.f );
// Scroll down by 150px
scrollView->getVerticalScrollBar()->setValue( 150.f / actualMaxScrollY );
Vector2f worldPos2 = stickyChild->convertToWorldSpace( { 0, 0 } );
EXPECT_NEAR( 20.f, worldPos2.y, 1.f );
Engine::destroySingleton();
}

View File

@@ -1173,3 +1173,71 @@ UTEST( UIHTML, InlineBlockBrowserTest ) {
Engine::destroySingleton();
}
UTEST( UIHTML, HeightExpansion ) {
Engine::instance()->createWindow( WindowSettings( 1024, 653, "Height Expansion Test",
WindowStyle::Default, WindowBackend::Default,
32, {}, 1, false, true ),
ContextSettings( false, 0, 0, GLv_default, true, false ) );
UI::UISceneNode* sceneNode = init_test_inline_block();
sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/ensoft/" );
std::string html;
FileSystem::fileGet( "assets/html/ensoft/ensoft.html", html );
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
sceneNode->update( Seconds( 1 ) );
// Wait a bit and update again to make sure layouts are computed
sceneNode->updateDirtyLayouts();
auto htmlNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_HTML );
auto bodyNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY );
ASSERT_TRUE( htmlNode != nullptr );
ASSERT_TRUE( bodyNode != nullptr );
auto htmlWidget = htmlNode->asType<UIWidget>();
auto bodyWidget = bodyNode->asType<UIWidget>();
EXPECT_GT( htmlWidget->getSize().getHeight(), 0 );
EXPECT_GT( bodyWidget->getSize().getHeight(), 0 );
EXPECT_GE( htmlWidget->getSize().getHeight(), bodyWidget->getSize().getHeight() );
Engine::destroySingleton();
}
UTEST( UIHTML, HeightExpansion_FixedDoesNotExpand ) {
Engine::instance()->createWindow( WindowSettings( 1024, 653, "Height Expansion Test",
WindowStyle::Default, WindowBackend::Default,
32, {}, 1, false, true ),
ContextSettings( false, 0, 0, GLv_default, true, false ) );
UI::UISceneNode* sceneNode = init_test_inline_block();
const std::string html = R"HTML(
<!DOCTYPE html>
<html>
<body style="margin: 0; padding: 0;">
<div style="height: 100px;"></div>
<div style="position: fixed; top: 500px; height: 50px;"></div>
</body>
</html>
)HTML";
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
sceneNode->update( Seconds( 1 ) );
sceneNode->updateDirtyLayouts();
auto bodyNode = sceneNode->getRoot()->findByType( UI_TYPE_HTML_BODY );
ASSERT_TRUE( bodyNode != nullptr );
auto bodyWidget = bodyNode->asType<UIWidget>();
// The height should be 100px, not 550px because the fixed div should be ignored.
EXPECT_NEAR( bodyWidget->getPixelsSize().getHeight(), 100.f, 1.f );
Engine::destroySingleton();
}